From 539e06fd2a14e857281b0521b4817fb4f23d34da Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 01:25:27 +0000 Subject: [PATCH 1/6] refactor(ve2): retire per-family state contracts for the literal ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `dropdownItemActive` report is right about the facts and wrong about the cause. The four contracts were not forgotten — they are the unwired remainder of a whole category. A per-family alias collapses a compound source selector into one identifier: `.dropdown-item.active` → `dropdownItemActive`, `.btn.show` → `btnShowHook`. 33 identifiers stood for 9 Bootstrap state classes. The literal emitter translates 1:1, so each alias needed a divergence-manifest entry to exist in the CSS at all — 12 remapSymbols plus 8 mirrors, hand-written, one per family that happened to have a component demanding it. Families without a demo never got an entry, so 18 of the 33 shipped with no CSS behind them. Nothing compared the two hand-maintained lists. Emitting both (the report's first suggestion) is the addMirrorRule pattern that already produced 8 of those entries; it would take the list from 15 to 19 and keep the failure mode. An alias is a pure rename — `${navLink}${active}` has identical specificity and match set — so it buys no isolation the compound selector doesn't already give. Plan §8.6 said this; T8 reverted it on the wrong side by teaching the emitter to produce the aliases instead of regenerating the CSS. - divergence manifest 22 → 2 entries (body-split, legend-element-mirror) - 36 contracts deleted: the 33 aliases, tooltipVe/popoverVe, and the duplicate fade/show/collapse copies in ui/navs, ui/modal, ui/navbar. literal/ is now the single owner of every shared state token. - kept modalOpenHook and carouselSlide: `.modal-open` and `.slide` have no rule in any bootstrap.css, so they are runtime markers, not aliases - 27 themes regenerated onto ${component}${state}: −4290 lines, the per-family .fade / .fade:not(.show) mirrors collapse into one generic rule each - 8 adapters + ~90 components stamp the literal hashes, dropping the double-stamp workarounds that had accumulated in 58 files Gate: check-contract-wiring.mjs (static, pre-build) plus a liveness assertion in build-contract.mjs (authoritative, real hashes vs emitted CSS). It keys on (module, symbol), not name — `fade` is declared twice and a name-keyed check reads the live copy as proof the dead one is fine. Deliberate inertness goes in contract-wiring-allowlist.mjs with a reason; a state hook never qualifies. Two further bugs the gate surfaced: build-contract only checked liveness for duplicate candidates, so a name declared by exactly one module was published unchecked — the precise mechanism that shipped the four; and contractModules() filtered by filename, excluding layout/container.css.ts, so containerFluid resolved to literal's dead copy while the themes styled layout's. Also regenerates theme-runtime.ts, which still imported the utilities/used family retired in dd753ef4 — ve-project2 did not build without it. Package dead exports 90 → 68, all allowlisted. `active` keeps hash b17c3vgbe3. Verified by rule-set equivalence across all 27 themes: 0 distinct rules dropped, 0 added (scripts/_cssdiff.mjs). BREAKING CHANGE: `dropdownItemActive`, `listGroupItemActive`, `navLinkActive` and the other per-family state aliases are gone. Compose the literal contract instead: `cx(dropdownItem, isActive && active)`, with `active` from the package's `global` entry. The removed names now fail to compile rather than failing to paint. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DjPPs9bCYcKGGVAhbzQNX4 --- ...08-09-retire-per-family-state-contracts.md | 125 ++++++++++ docs/ve-architecture.md | 7 + docs/ve2-literal-conversion-plan.md | 27 +++ docs/ve2-theme-generator-audit-playbook.md | 2 +- package.json | 3 +- scripts/_cssdiff.mjs | Bin 0 -> 3699 bytes scripts/build-contract.mjs | 50 +++- scripts/check-contract-wiring.mjs | 215 ++++++++++++++++++ scripts/contract-wiring-allowlist.mjs | 116 ++++++++++ .../divergence-manifest.mjs | 206 +---------------- scripts/generate-ve-literal/emit.mjs | 4 +- scripts/generate-ve-theme/css-utils.mjs | 15 +- .../ui/accordion/AccordionExample.tsx | 11 +- .../components/ui/accordion/ve-collapse.ts | 3 +- .../src/components/ui/alerts/DangerAlert.tsx | 5 +- .../src/components/ui/alerts/DarkAlert.tsx | 5 +- .../src/components/ui/alerts/InfoAlert.tsx | 5 +- .../src/components/ui/alerts/LightAlert.tsx | 5 +- .../src/components/ui/alerts/PrimaryAlert.tsx | 5 +- .../components/ui/alerts/SecondaryAlert.tsx | 5 +- .../src/components/ui/alerts/SuccessAlert.tsx | 5 +- .../src/components/ui/alerts/WarningAlert.tsx | 5 +- .../ui/breadcrumb/BreadcrumbExample.tsx | 13 +- .../OutlineCheckActiveDangerButton.tsx | 3 +- .../OutlineCheckActiveDarkButton.tsx | 3 +- .../OutlineCheckActiveInfoButton.tsx | 3 +- .../OutlineCheckActiveLightButton.tsx | 3 +- .../OutlineCheckActivePrimaryButton.tsx | 3 +- .../OutlineCheckActiveSecondaryButton.tsx | 3 +- .../OutlineCheckActiveSuccessButton.tsx | 3 +- .../OutlineCheckActiveWarningButton.tsx | 3 +- ...OutlineCheckActiveDisabledDangerButton.tsx | 3 +- .../OutlineCheckActiveDisabledDarkButton.tsx | 3 +- .../OutlineCheckActiveDisabledInfoButton.tsx | 3 +- .../OutlineCheckActiveDisabledLightButton.tsx | 3 +- ...utlineCheckActiveDisabledPrimaryButton.tsx | 3 +- ...lineCheckActiveDisabledSecondaryButton.tsx | 3 +- ...utlineCheckActiveDisabledSuccessButton.tsx | 3 +- ...utlineCheckActiveDisabledWarningButton.tsx | 3 +- .../check-active/CheckActiveLargeButton.tsx | 3 +- .../check-active/CheckActiveSmallButton.tsx | 3 +- .../CheckActiveDisabledLargeButton.tsx | 3 +- .../CheckActiveDisabledSmallButton.tsx | 3 +- .../SolidCheckActiveBaseButton.tsx | 8 +- .../SolidCheckActiveDangerButton.tsx | 3 +- .../SolidCheckActiveDarkButton.tsx | 3 +- .../SolidCheckActiveInfoButton.tsx | 3 +- .../SolidCheckActiveLightButton.tsx | 3 +- .../SolidCheckActiveLinkButton.tsx | 3 +- .../SolidCheckActivePrimaryButton.tsx | 3 +- .../SolidCheckActiveSecondaryButton.tsx | 3 +- .../SolidCheckActiveSuccessButton.tsx | 3 +- .../SolidCheckActiveWarningButton.tsx | 3 +- .../SolidCheckActiveDisabledBaseButton.tsx | 8 +- .../SolidCheckActiveDisabledDangerButton.tsx | 3 +- .../SolidCheckActiveDisabledDarkButton.tsx | 3 +- .../SolidCheckActiveDisabledInfoButton.tsx | 3 +- .../SolidCheckActiveDisabledLightButton.tsx | 3 +- .../SolidCheckActiveDisabledLinkButton.tsx | 3 +- .../SolidCheckActiveDisabledPrimaryButton.tsx | 3 +- ...olidCheckActiveDisabledSecondaryButton.tsx | 3 +- .../SolidCheckActiveDisabledSuccessButton.tsx | 3 +- .../SolidCheckActiveDisabledWarningButton.tsx | 3 +- .../ui/card-tabs/CardTabsExample.tsx | 10 +- .../ui/carousel/CarouselExample.tsx | 10 +- .../src/components/ui/carousel/ve-carousel.ts | 4 +- .../ui/dropdowns/AlignedDropdown.tsx | 8 +- .../components/ui/dropdowns/EndDropdown.tsx | 8 +- .../components/ui/dropdowns/LargeDropdown.tsx | 8 +- .../ui/dropdowns/NormalDropdown.tsx | 8 +- .../components/ui/dropdowns/SmallDropdown.tsx | 8 +- .../ui/dropdowns/SplitDangerDropdown.tsx | 4 - .../ui/dropdowns/SplitDarkDropdown.tsx | 4 - .../ui/dropdowns/SplitInfoDropdown.tsx | 4 - .../ui/dropdowns/SplitLightDropdown.tsx | 4 - .../ui/dropdowns/SplitPrimaryDropdown.tsx | 4 - .../ui/dropdowns/SplitSecondaryDropdown.tsx | 4 - .../ui/dropdowns/SplitSuccessDropdown.tsx | 4 - .../ui/dropdowns/SplitWarningDropdown.tsx | 4 - .../components/ui/dropdowns/StartDropdown.tsx | 8 +- .../components/ui/dropdowns/UpDropdown.tsx | 8 +- .../components/ui/dropdowns/ve-dropdown.ts | 10 +- .../ui/list-group/DefaultListGroup.tsx | 11 +- .../ui/modal/CenteredScrollableModal.tsx | 8 +- .../src/components/ui/modal/DefaultModal.tsx | 8 +- .../components/ui/modal/FullscreenModal.tsx | 8 +- .../ui/modal/StaticBackdropModal.tsx | 8 +- .../src/components/ui/modal/ve-modal.ts | 14 +- .../components/ui/navbar/NavbarExample.tsx | 17 +- .../src/components/ui/navs/BasicNav.tsx | 12 +- .../src/components/ui/navs/PillNav.tsx | 15 +- .../src/components/ui/navs/TabbedNav.tsx | 12 +- .../components/ui/offcanvas/ve-offcanvas.ts | 3 +- .../ui/pagination/LargePagination.tsx | 10 +- .../ui/pagination/SmallPagination.tsx | 7 +- .../components/ui/popovers/BasicPopover.tsx | 8 +- .../components/ui/popovers/BottomPopover.tsx | 7 +- .../src/components/ui/popovers/EndPopover.tsx | 7 +- .../components/ui/popovers/StartPopover.tsx | 7 +- .../src/components/ui/popovers/TopPopover.tsx | 7 +- .../src/components/ui/popovers/ve-popover.ts | 4 +- .../ui/scrollspy/ScrollspyExample.tsx | 17 +- .../src/components/ui/toasts/ToastExample.tsx | 6 +- .../src/components/ui/toasts/ve-toast.ts | 16 +- .../components/ui/tooltips/BottomTooltip.tsx | 14 +- .../src/components/ui/tooltips/EndTooltip.tsx | 14 +- .../components/ui/tooltips/HtmlTooltip.tsx | 10 +- .../components/ui/tooltips/StartTooltip.tsx | 14 +- .../src/components/ui/tooltips/TopTooltip.tsx | 14 +- .../src/components/ui/tooltips/ve-tooltip.ts | 4 +- .../ui/accordion/contract.css.ts | 3 - .../theme-contract/ui/alerts/contract.css.ts | 1 - .../ui/breadcrumb/contract.css.ts | 3 - .../theme-contract/ui/buttons/contract.css.ts | 4 - .../ui/carousel/contract.css.ts | 3 - .../ui/dropdowns/contract.css.ts | 4 - .../ui/list-group/contract.css.ts | 2 - .../theme-contract/ui/modal/contract.css.ts | 3 - .../theme-contract/ui/navbar/contract.css.ts | 2 - .../theme-contract/ui/navs/contract.css.ts | 8 - .../ui/pagination/contract.css.ts | 2 - .../ui/popovers/contract.css.ts | 3 - .../theme-contract/ui/toasts/contract.css.ts | 4 - .../ui/tooltips/contract.css.ts | 3 - .../src/theme-runtime/theme-runtime.ts | 27 --- .../src/themes/bootstrap/global/styles.css.ts | 5 +- .../themes/bootstrap/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/bootstrap/theme.ts | 1 - .../bootstrap/ui/accordion/styles.css.ts | 12 +- .../themes/bootstrap/ui/buttons/styles.css.ts | 2 +- .../bootstrap/ui/dropdowns/styles.css.ts | 22 +- .../themes/bootstrap/ui/modal/styles.css.ts | 30 +-- .../themes/bootstrap/ui/navs/styles.css.ts | 17 +- .../bootstrap/ui/offcanvas/styles.css.ts | 3 +- .../bootstrap/ui/pagination/styles.css.ts | 11 +- .../bootstrap/ui/popovers/styles.css.ts | 18 -- .../themes/bootstrap/ui/toasts/styles.css.ts | 14 +- .../bootstrap/ui/tooltips/styles.css.ts | 26 +-- .../themes/bootstrap/utilities/styles.css.ts | 6 + .../src/themes/brite/global/styles.css.ts | 5 +- .../src/themes/brite/literal/styles.css.ts | 144 +++--------- ve-project2/src/themes/brite/theme.ts | 1 - .../themes/brite/ui/accordion/styles.css.ts | 12 +- .../src/themes/brite/ui/buttons/styles.css.ts | 2 +- .../themes/brite/ui/dropdowns/styles.css.ts | 28 +-- .../src/themes/brite/ui/modal/styles.css.ts | 30 +-- .../src/themes/brite/ui/navs/styles.css.ts | 21 +- .../themes/brite/ui/offcanvas/styles.css.ts | 3 +- .../themes/brite/ui/pagination/styles.css.ts | 11 +- .../themes/brite/ui/popovers/styles.css.ts | 18 -- .../src/themes/brite/ui/toasts/styles.css.ts | 14 +- .../themes/brite/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/brite/utilities/styles.css.ts | 6 + .../src/themes/cerulean/global/styles.css.ts | 5 +- .../src/themes/cerulean/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/cerulean/theme.ts | 1 - .../cerulean/ui/accordion/styles.css.ts | 12 +- .../themes/cerulean/ui/buttons/styles.css.ts | 2 +- .../cerulean/ui/dropdowns/styles.css.ts | 22 +- .../themes/cerulean/ui/modal/styles.css.ts | 30 +-- .../src/themes/cerulean/ui/navs/styles.css.ts | 17 +- .../cerulean/ui/offcanvas/styles.css.ts | 3 +- .../cerulean/ui/pagination/styles.css.ts | 11 +- .../themes/cerulean/ui/popovers/styles.css.ts | 18 -- .../themes/cerulean/ui/toasts/styles.css.ts | 14 +- .../themes/cerulean/ui/tooltips/styles.css.ts | 26 +-- .../themes/cerulean/utilities/styles.css.ts | 6 + .../src/themes/cosmo/global/styles.css.ts | 5 +- .../src/themes/cosmo/literal/styles.css.ts | 133 +++-------- ve-project2/src/themes/cosmo/theme.ts | 1 - .../themes/cosmo/ui/accordion/styles.css.ts | 7 +- .../src/themes/cosmo/ui/buttons/styles.css.ts | 2 +- .../themes/cosmo/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/cosmo/ui/modal/styles.css.ts | 30 +-- .../src/themes/cosmo/ui/navs/styles.css.ts | 17 +- .../themes/cosmo/ui/offcanvas/styles.css.ts | 3 +- .../themes/cosmo/ui/pagination/styles.css.ts | 11 +- .../themes/cosmo/ui/popovers/styles.css.ts | 18 -- .../src/themes/cosmo/ui/toasts/styles.css.ts | 14 +- .../themes/cosmo/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/cyborg/global/styles.css.ts | 5 +- .../src/themes/cyborg/literal/styles.css.ts | 146 +++--------- ve-project2/src/themes/cyborg/theme.ts | 1 - .../themes/cyborg/ui/accordion/styles.css.ts | 12 +- .../themes/cyborg/ui/buttons/styles.css.ts | 2 +- .../themes/cyborg/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/cyborg/ui/modal/styles.css.ts | 30 +-- .../src/themes/cyborg/ui/navs/styles.css.ts | 29 ++- .../themes/cyborg/ui/offcanvas/styles.css.ts | 3 +- .../themes/cyborg/ui/pagination/styles.css.ts | 11 +- .../themes/cyborg/ui/popovers/styles.css.ts | 18 -- .../src/themes/cyborg/ui/toasts/styles.css.ts | 14 +- .../themes/cyborg/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/cyborg/utilities/styles.css.ts | 6 + .../src/themes/darkly/global/styles.css.ts | 5 +- .../src/themes/darkly/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/darkly/theme.ts | 1 - .../themes/darkly/ui/accordion/styles.css.ts | 12 +- .../themes/darkly/ui/buttons/styles.css.ts | 2 +- .../themes/darkly/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/darkly/ui/modal/styles.css.ts | 30 +-- .../src/themes/darkly/ui/navs/styles.css.ts | 17 +- .../themes/darkly/ui/offcanvas/styles.css.ts | 3 +- .../themes/darkly/ui/pagination/styles.css.ts | 11 +- .../themes/darkly/ui/popovers/styles.css.ts | 18 -- .../src/themes/darkly/ui/toasts/styles.css.ts | 14 +- .../themes/darkly/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/darkly/utilities/styles.css.ts | 6 + .../src/themes/flatly/global/styles.css.ts | 5 +- .../src/themes/flatly/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/flatly/theme.ts | 1 - .../themes/flatly/ui/accordion/styles.css.ts | 12 +- .../themes/flatly/ui/buttons/styles.css.ts | 2 +- .../themes/flatly/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/flatly/ui/modal/styles.css.ts | 30 +-- .../src/themes/flatly/ui/navs/styles.css.ts | 17 +- .../themes/flatly/ui/offcanvas/styles.css.ts | 3 +- .../themes/flatly/ui/pagination/styles.css.ts | 11 +- .../themes/flatly/ui/popovers/styles.css.ts | 18 -- .../src/themes/flatly/ui/toasts/styles.css.ts | 14 +- .../themes/flatly/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/flatly/utilities/styles.css.ts | 6 + .../src/themes/journal/global/styles.css.ts | 5 +- .../src/themes/journal/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/journal/theme.ts | 1 - .../themes/journal/ui/accordion/styles.css.ts | 12 +- .../themes/journal/ui/buttons/styles.css.ts | 2 +- .../themes/journal/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/journal/ui/modal/styles.css.ts | 30 +-- .../src/themes/journal/ui/navs/styles.css.ts | 17 +- .../themes/journal/ui/offcanvas/styles.css.ts | 3 +- .../journal/ui/pagination/styles.css.ts | 11 +- .../themes/journal/ui/popovers/styles.css.ts | 18 -- .../themes/journal/ui/toasts/styles.css.ts | 14 +- .../themes/journal/ui/tooltips/styles.css.ts | 26 +-- .../themes/journal/utilities/styles.css.ts | 6 + .../src/themes/litera/global/styles.css.ts | 5 +- .../src/themes/litera/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/litera/theme.ts | 1 - .../themes/litera/ui/accordion/styles.css.ts | 12 +- .../themes/litera/ui/buttons/styles.css.ts | 2 +- .../themes/litera/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/litera/ui/modal/styles.css.ts | 30 +-- .../src/themes/litera/ui/navs/styles.css.ts | 17 +- .../themes/litera/ui/offcanvas/styles.css.ts | 3 +- .../themes/litera/ui/pagination/styles.css.ts | 11 +- .../themes/litera/ui/popovers/styles.css.ts | 18 -- .../src/themes/litera/ui/toasts/styles.css.ts | 14 +- .../themes/litera/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/litera/utilities/styles.css.ts | 6 + .../src/themes/lumen/global/styles.css.ts | 5 +- .../src/themes/lumen/literal/styles.css.ts | 142 +++--------- ve-project2/src/themes/lumen/theme.ts | 1 - .../themes/lumen/ui/accordion/styles.css.ts | 12 +- .../src/themes/lumen/ui/buttons/styles.css.ts | 2 +- .../themes/lumen/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/lumen/ui/modal/styles.css.ts | 30 +-- .../src/themes/lumen/ui/navs/styles.css.ts | 24 +- .../themes/lumen/ui/offcanvas/styles.css.ts | 3 +- .../themes/lumen/ui/pagination/styles.css.ts | 12 +- .../themes/lumen/ui/popovers/styles.css.ts | 18 -- .../src/themes/lumen/ui/toasts/styles.css.ts | 14 +- .../themes/lumen/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/lumen/utilities/styles.css.ts | 6 + .../src/themes/lux/global/styles.css.ts | 5 +- .../src/themes/lux/literal/styles.css.ts | 133 +++-------- ve-project2/src/themes/lux/theme.ts | 1 - .../src/themes/lux/ui/accordion/styles.css.ts | 7 +- .../src/themes/lux/ui/buttons/styles.css.ts | 2 +- .../src/themes/lux/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/lux/ui/modal/styles.css.ts | 30 +-- .../src/themes/lux/ui/navs/styles.css.ts | 17 +- .../src/themes/lux/ui/offcanvas/styles.css.ts | 3 +- .../themes/lux/ui/pagination/styles.css.ts | 11 +- .../src/themes/lux/ui/popovers/styles.css.ts | 18 -- .../src/themes/lux/ui/toasts/styles.css.ts | 14 +- .../src/themes/lux/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/materia/global/styles.css.ts | 5 +- .../src/themes/materia/literal/styles.css.ts | 151 +++--------- ve-project2/src/themes/materia/theme.ts | 1 - .../themes/materia/ui/accordion/styles.css.ts | 12 +- .../themes/materia/ui/buttons/styles.css.ts | 2 +- .../themes/materia/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/materia/ui/modal/styles.css.ts | 30 +-- .../src/themes/materia/ui/navs/styles.css.ts | 33 ++- .../themes/materia/ui/offcanvas/styles.css.ts | 3 +- .../materia/ui/pagination/styles.css.ts | 11 +- .../themes/materia/ui/popovers/styles.css.ts | 18 -- .../themes/materia/ui/toasts/styles.css.ts | 14 +- .../themes/materia/ui/tooltips/styles.css.ts | 26 +-- .../themes/materia/utilities/styles.css.ts | 6 + .../src/themes/minty/global/styles.css.ts | 5 +- .../src/themes/minty/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/minty/theme.ts | 1 - .../themes/minty/ui/accordion/styles.css.ts | 12 +- .../src/themes/minty/ui/buttons/styles.css.ts | 2 +- .../themes/minty/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/minty/ui/modal/styles.css.ts | 30 +-- .../src/themes/minty/ui/navs/styles.css.ts | 17 +- .../themes/minty/ui/offcanvas/styles.css.ts | 3 +- .../themes/minty/ui/pagination/styles.css.ts | 11 +- .../themes/minty/ui/popovers/styles.css.ts | 18 -- .../src/themes/minty/ui/toasts/styles.css.ts | 14 +- .../themes/minty/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/minty/utilities/styles.css.ts | 6 + .../src/themes/morph/global/styles.css.ts | 5 +- .../src/themes/morph/literal/styles.css.ts | 141 +++--------- ve-project2/src/themes/morph/theme.ts | 1 - .../themes/morph/ui/accordion/styles.css.ts | 12 +- .../src/themes/morph/ui/buttons/styles.css.ts | 2 +- .../themes/morph/ui/dropdowns/styles.css.ts | 26 +-- .../src/themes/morph/ui/modal/styles.css.ts | 30 +-- .../src/themes/morph/ui/navs/styles.css.ts | 21 +- .../themes/morph/ui/offcanvas/styles.css.ts | 3 +- .../themes/morph/ui/pagination/styles.css.ts | 11 +- .../themes/morph/ui/popovers/styles.css.ts | 18 -- .../src/themes/morph/ui/toasts/styles.css.ts | 14 +- .../themes/morph/ui/tooltips/styles.css.ts | 28 +-- .../src/themes/morph/utilities/styles.css.ts | 6 + .../src/themes/pulse/global/styles.css.ts | 5 +- .../src/themes/pulse/literal/styles.css.ts | 141 +++--------- ve-project2/src/themes/pulse/theme.ts | 1 - .../themes/pulse/ui/accordion/styles.css.ts | 7 +- .../src/themes/pulse/ui/buttons/styles.css.ts | 2 +- .../themes/pulse/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/pulse/ui/modal/styles.css.ts | 30 +-- .../src/themes/pulse/ui/navs/styles.css.ts | 25 +- .../themes/pulse/ui/offcanvas/styles.css.ts | 3 +- .../themes/pulse/ui/pagination/styles.css.ts | 11 +- .../themes/pulse/ui/popovers/styles.css.ts | 18 -- .../src/themes/pulse/ui/toasts/styles.css.ts | 14 +- .../themes/pulse/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/quartz/global/styles.css.ts | 5 +- .../src/themes/quartz/literal/styles.css.ts | 146 +++--------- ve-project2/src/themes/quartz/theme.ts | 1 - .../themes/quartz/ui/accordion/styles.css.ts | 12 +- .../themes/quartz/ui/buttons/styles.css.ts | 2 +- .../themes/quartz/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/quartz/ui/modal/styles.css.ts | 30 +-- .../src/themes/quartz/ui/navs/styles.css.ts | 27 ++- .../themes/quartz/ui/offcanvas/styles.css.ts | 3 +- .../themes/quartz/ui/pagination/styles.css.ts | 11 +- .../themes/quartz/ui/popovers/styles.css.ts | 18 -- .../src/themes/quartz/ui/toasts/styles.css.ts | 14 +- .../themes/quartz/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/quartz/utilities/styles.css.ts | 18 +- .../src/themes/sandstone/global/styles.css.ts | 5 +- .../themes/sandstone/literal/styles.css.ts | 146 +++--------- ve-project2/src/themes/sandstone/theme.ts | 1 - .../sandstone/ui/accordion/styles.css.ts | 12 +- .../themes/sandstone/ui/buttons/styles.css.ts | 2 +- .../sandstone/ui/dropdowns/styles.css.ts | 22 +- .../themes/sandstone/ui/modal/styles.css.ts | 30 +-- .../themes/sandstone/ui/navs/styles.css.ts | 29 ++- .../sandstone/ui/offcanvas/styles.css.ts | 3 +- .../sandstone/ui/pagination/styles.css.ts | 11 +- .../sandstone/ui/popovers/styles.css.ts | 18 -- .../themes/sandstone/ui/toasts/styles.css.ts | 14 +- .../sandstone/ui/tooltips/styles.css.ts | 26 +-- .../themes/sandstone/utilities/styles.css.ts | 6 + .../src/themes/simplex/global/styles.css.ts | 5 +- .../src/themes/simplex/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/simplex/theme.ts | 1 - .../themes/simplex/ui/accordion/styles.css.ts | 12 +- .../themes/simplex/ui/buttons/styles.css.ts | 2 +- .../themes/simplex/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/simplex/ui/modal/styles.css.ts | 30 +-- .../src/themes/simplex/ui/navs/styles.css.ts | 17 +- .../themes/simplex/ui/offcanvas/styles.css.ts | 3 +- .../simplex/ui/pagination/styles.css.ts | 11 +- .../themes/simplex/ui/popovers/styles.css.ts | 18 -- .../themes/simplex/ui/toasts/styles.css.ts | 14 +- .../themes/simplex/ui/tooltips/styles.css.ts | 26 +-- .../themes/simplex/utilities/styles.css.ts | 6 + .../src/themes/sketchy/global/styles.css.ts | 5 +- .../src/themes/sketchy/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/sketchy/theme.ts | 1 - .../themes/sketchy/ui/accordion/styles.css.ts | 12 +- .../themes/sketchy/ui/buttons/styles.css.ts | 2 +- .../themes/sketchy/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/sketchy/ui/modal/styles.css.ts | 30 +-- .../src/themes/sketchy/ui/navs/styles.css.ts | 17 +- .../themes/sketchy/ui/offcanvas/styles.css.ts | 3 +- .../sketchy/ui/pagination/styles.css.ts | 11 +- .../themes/sketchy/ui/popovers/styles.css.ts | 18 -- .../themes/sketchy/ui/toasts/styles.css.ts | 14 +- .../themes/sketchy/ui/tooltips/styles.css.ts | 26 +-- .../themes/sketchy/utilities/styles.css.ts | 6 + .../src/themes/slate/global/styles.css.ts | 5 +- .../src/themes/slate/literal/styles.css.ts | 162 ++++--------- ve-project2/src/themes/slate/theme.ts | 1 - .../themes/slate/ui/accordion/styles.css.ts | 12 +- .../src/themes/slate/ui/buttons/styles.css.ts | 2 +- .../themes/slate/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/slate/ui/modal/styles.css.ts | 30 +-- .../src/themes/slate/ui/navs/styles.css.ts | 40 ++-- .../themes/slate/ui/offcanvas/styles.css.ts | 3 +- .../themes/slate/ui/pagination/styles.css.ts | 15 +- .../themes/slate/ui/popovers/styles.css.ts | 18 -- .../src/themes/slate/ui/toasts/styles.css.ts | 14 +- .../themes/slate/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/slate/utilities/styles.css.ts | 6 + .../src/themes/solar/global/styles.css.ts | 5 +- .../src/themes/solar/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/solar/theme.ts | 1 - .../themes/solar/ui/accordion/styles.css.ts | 12 +- .../src/themes/solar/ui/buttons/styles.css.ts | 2 +- .../themes/solar/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/solar/ui/modal/styles.css.ts | 30 +-- .../src/themes/solar/ui/navs/styles.css.ts | 17 +- .../themes/solar/ui/offcanvas/styles.css.ts | 3 +- .../themes/solar/ui/pagination/styles.css.ts | 11 +- .../themes/solar/ui/popovers/styles.css.ts | 18 -- .../src/themes/solar/ui/toasts/styles.css.ts | 14 +- .../themes/solar/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/solar/utilities/styles.css.ts | 6 + .../src/themes/spacelab/global/styles.css.ts | 5 +- .../src/themes/spacelab/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/spacelab/theme.ts | 1 - .../spacelab/ui/accordion/styles.css.ts | 12 +- .../themes/spacelab/ui/buttons/styles.css.ts | 2 +- .../spacelab/ui/dropdowns/styles.css.ts | 22 +- .../themes/spacelab/ui/modal/styles.css.ts | 30 +-- .../src/themes/spacelab/ui/navs/styles.css.ts | 17 +- .../spacelab/ui/offcanvas/styles.css.ts | 3 +- .../spacelab/ui/pagination/styles.css.ts | 11 +- .../themes/spacelab/ui/popovers/styles.css.ts | 18 -- .../themes/spacelab/ui/toasts/styles.css.ts | 14 +- .../themes/spacelab/ui/tooltips/styles.css.ts | 26 +-- .../themes/spacelab/utilities/styles.css.ts | 6 + .../src/themes/superhero/global/styles.css.ts | 5 +- .../themes/superhero/literal/styles.css.ts | 138 +++-------- ve-project2/src/themes/superhero/theme.ts | 1 - .../superhero/ui/accordion/styles.css.ts | 12 +- .../themes/superhero/ui/buttons/styles.css.ts | 2 +- .../superhero/ui/dropdowns/styles.css.ts | 22 +- .../themes/superhero/ui/modal/styles.css.ts | 30 +-- .../themes/superhero/ui/navs/styles.css.ts | 21 +- .../superhero/ui/offcanvas/styles.css.ts | 3 +- .../superhero/ui/pagination/styles.css.ts | 11 +- .../superhero/ui/popovers/styles.css.ts | 18 -- .../themes/superhero/ui/toasts/styles.css.ts | 14 +- .../superhero/ui/tooltips/styles.css.ts | 26 +-- .../themes/superhero/utilities/styles.css.ts | 6 + .../src/themes/united/global/styles.css.ts | 5 +- .../src/themes/united/literal/styles.css.ts | 134 +++-------- ve-project2/src/themes/united/theme.ts | 1 - .../themes/united/ui/accordion/styles.css.ts | 12 +- .../themes/united/ui/buttons/styles.css.ts | 2 +- .../themes/united/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/united/ui/modal/styles.css.ts | 30 +-- .../src/themes/united/ui/navs/styles.css.ts | 17 +- .../themes/united/ui/offcanvas/styles.css.ts | 3 +- .../themes/united/ui/pagination/styles.css.ts | 11 +- .../themes/united/ui/popovers/styles.css.ts | 18 -- .../src/themes/united/ui/toasts/styles.css.ts | 14 +- .../themes/united/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/united/utilities/styles.css.ts | 6 + .../src/themes/vapor/global/styles.css.ts | 5 +- .../src/themes/vapor/literal/styles.css.ts | 144 +++--------- ve-project2/src/themes/vapor/theme.ts | 1 - .../themes/vapor/ui/accordion/styles.css.ts | 12 +- .../src/themes/vapor/ui/buttons/styles.css.ts | 2 +- .../themes/vapor/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/vapor/ui/modal/styles.css.ts | 30 +-- .../src/themes/vapor/ui/navs/styles.css.ts | 25 +- .../themes/vapor/ui/offcanvas/styles.css.ts | 3 +- .../themes/vapor/ui/pagination/styles.css.ts | 13 +- .../themes/vapor/ui/popovers/styles.css.ts | 18 -- .../src/themes/vapor/ui/toasts/styles.css.ts | 14 +- .../themes/vapor/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/vapor/utilities/styles.css.ts | 6 + .../src/themes/yeti/global/styles.css.ts | 5 +- .../src/themes/yeti/literal/styles.css.ts | 136 +++-------- ve-project2/src/themes/yeti/theme.ts | 1 - .../themes/yeti/ui/accordion/styles.css.ts | 12 +- .../src/themes/yeti/ui/buttons/styles.css.ts | 2 +- .../themes/yeti/ui/dropdowns/styles.css.ts | 22 +- .../src/themes/yeti/ui/modal/styles.css.ts | 30 +-- .../src/themes/yeti/ui/navs/styles.css.ts | 17 +- .../themes/yeti/ui/offcanvas/styles.css.ts | 3 +- .../themes/yeti/ui/pagination/styles.css.ts | 13 +- .../src/themes/yeti/ui/popovers/styles.css.ts | 18 -- .../src/themes/yeti/ui/toasts/styles.css.ts | 14 +- .../src/themes/yeti/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/yeti/utilities/styles.css.ts | 6 + .../src/themes/zephyr/global/styles.css.ts | 5 +- .../src/themes/zephyr/literal/styles.css.ts | 141 +++--------- ve-project2/src/themes/zephyr/theme.ts | 1 - .../themes/zephyr/ui/accordion/styles.css.ts | 12 +- .../themes/zephyr/ui/buttons/styles.css.ts | 2 +- .../themes/zephyr/ui/dropdowns/styles.css.ts | 26 +-- .../src/themes/zephyr/ui/modal/styles.css.ts | 30 +-- .../src/themes/zephyr/ui/navs/styles.css.ts | 23 +- .../themes/zephyr/ui/offcanvas/styles.css.ts | 3 +- .../themes/zephyr/ui/pagination/styles.css.ts | 11 +- .../themes/zephyr/ui/popovers/styles.css.ts | 18 -- .../src/themes/zephyr/ui/toasts/styles.css.ts | 14 +- .../themes/zephyr/ui/tooltips/styles.css.ts | 26 +-- .../src/themes/zephyr/utilities/styles.css.ts | 6 + 500 files changed, 2768 insertions(+), 7054 deletions(-) create mode 100644 docs/changelog/2026-08-09-retire-per-family-state-contracts.md create mode 100644 scripts/_cssdiff.mjs create mode 100644 scripts/check-contract-wiring.mjs create mode 100644 scripts/contract-wiring-allowlist.mjs diff --git a/docs/changelog/2026-08-09-retire-per-family-state-contracts.md b/docs/changelog/2026-08-09-retire-per-family-state-contracts.md new file mode 100644 index 000000000..e761772da --- /dev/null +++ b/docs/changelog/2026-08-09-retire-per-family-state-contracts.md @@ -0,0 +1,125 @@ +# 2026-08-09 — Aposentar os contratos de estado por família (T11) + +## Prompt original + +> veja este relatório. Não quero simplesmente aplicar uma das sugestões dele, mas quero ver qual a +> solução mais idiomática, que segue melhor o princípio de converter o bootswatch o mais +> literalmente possível para o Vanilla Extract, mesmo que isso quebre a compatibilidade retroativa. + +Relatório: `dropdownItemActive`, `dropdownItemDisabled`, `listGroupItemActive` e +`listGroupItemDisabled` são exportados por `@arijs/bootswatch-ve@0.2.0` e referenciados por nenhum +dos 27 temas. Aplicar um deles é um no-op silencioso. + +## Diagnóstico + +Os fatos do relatório conferem; a causa não. Os quatro não foram esquecidos — são o resto não-ligado +de uma **categoria inteira**. + +Um apelido por família colapsa um seletor composto num identificador só: `.dropdown-item.active` → +`dropdownItemActive`, `.btn.show` → `btnShowHook`, `.nav-link.active` → `navLinkActive`. Eram **33 +identificadores para 9 classes de estado do Bootstrap**. Como o emissor literal traduz 1:1, cada +apelido precisava de uma entrada no manifesto de divergências para existir no CSS: 12 `remapSymbols` +mais 8 espelhos, escritos à mão, um por família que tinha um componente exigindo. Famílias sem demo +(item de dropdown, item de list-group, breadcrumb, carousel, painel de tab, …) nunca ganharam +entrada, então **18 dos 33 eram publicados sem uma linha de CSS**. Nada comparava as duas listas +mantidas à mão. + +O histórico está no próprio plano: T6 unificou os estados conforme §8.6, T8 descobriu que o CSS de +modal/accordion/toast ainda usava os símbolos por família e **reverteu pelo lado errado** — manteve +os apelidos e ensinou o emissor a produzi-los, em vez de regenerar o CSS. + +A regra citada pelo relatório como invariante violada +(`docs/ve2-theme-generator-audit-playbook.md:208`) documenta o gerador v1, deletado no T10, e +*manda* fazer exatamente o anti-padrão. + +## Por que apagar, e não ligar + +A sugestão nº 1 do relatório (emitir os dois) é o padrão `addMirrorRule` que já existia e já tinha +produzido 8 entradas de manutenção. Ligar os quatro faltantes levaria a lista de 15 para 19 e +manteria o modo de falha. + +Um apelido por família é um **rename puro**: `${navLink}${navLinkActive}` e `${navLink}${active}` +têm especificidade idêntica e casam o mesmo conjunto. Não compram isolamento — o seletor composto já +isola (`${modal}${show}` não casa um dropdown) — e custam 20 entradas de manifesto, 33 identificadores +e a deriva que gerou o bug. + +## Mudanças + +- **Manifesto de divergências: 22 → 2 entradas.** Restam `body-split` (vazamento sob o backdrop) e + `legend-element-mirror`. O mecanismo `remapSymbols` fica para um caso real de interferência. +- **36 contratos removidos**: os 33 apelidos, mais `tooltipVe`/`popoverVe` (os templates já + carimbavam `tooltip`/`popover` vivos ao lado) e as cópias duplicadas de `fade`/`show`/`collapse` + em `ui/navs`, `ui/modal` e `ui/navbar`. `literal/` passa a ser dono único de cada token de estado. +- **Preservados**: `modalOpenHook` e `carouselSlide` — `.modal-open` e `.slide` não têm regra em + nenhum `bootstrap.css`, são marcadores de runtime, não apelidos. +- **27 temas regenerados**: −4 290 linhas de CSS gerado; os espelhos de `.fade` e `.fade:not(.show)` + por família viram uma regra genérica cada. +- **8 adapters + ~80 componentes** carimbam os hashes literais. `ve-dropdown` passa um único `show` + para `CLASS_NAME_SHOW_TRIGGER` e `CLASS_NAME_SHOW_MENU`; `ve-carousel` perde o carimbo duplo; + `ve-toast` larga `CLASS_NAME_HIDE`. + +## O gate + +`scripts/check-contract-wiring.mjs` (estático, pré-build) e uma asserção de liveness em +`build-contract.mjs` (autoritativa, hashes reais vs CSS emitido). Inércia deliberada é declarada em +`scripts/contract-wiring-allowlist.mjs` com motivo; um hook de estado nunca é entrada válida. + +Ele precisa chavear por **(módulo, símbolo)** e não por nome: `fade` é declarado em dois lugares e um +check por nome dá o par vivo como prova de que o morto está ok. + +## Dois bugs que o gate expôs + +1. `build-contract.mjs` só checava liveness para candidatas **duplicadas** — um nome declarado por um + único módulo era publicado sem checagem. É o mecanismo exato que publicou os quatro. +2. `contractModules()` filtrava por nome de arquivo e deixava `layout/container.css.ts` de fora, então + `containerFluid` caía para a cópia morta de `literal/` enquanto os temas estilizavam a de `layout/`. + +## Resultado + +| | antes | depois | +|---|---|---| +| exports mortos no pacote | 90 | 68 (todos na allowlist) | +| entradas no manifesto | 22 | 2 | +| nomes de contrato | 2240 | 2205 | + +`active` mantém o hash `b17c3vgbe3` — quem já compunha com ele não é afetado. + +## Quebra de compatibilidade + +`cx(dropdownItem, isActive && dropdownItemActive)` vira `cx(dropdownItem, isActive && active)`, com +`active` vindo do entry `global` do pacote. Os nomes removidos agora falham na compilação em vez de +falharem na pintura. + +## Verificação + +**Equivalência de CSS (a prova principal).** `scripts/_cssdiff.mjs` extrai todo `globalStyle(seletor, +{…})` dos chunks por família nos dois lados, reescreve os apelidos do lado antigo para os contratos +literais que eles representavam, e compara os conjuntos. Nos **27 temas: 0 regras distintas +removidas, 0 adicionadas**, contagem distinta idêntica. A contagem bruta cai exatamente 11 por tema — +as regras-espelho duplicadas colapsando na genérica. Como os apelidos eram renames puros (mesma +especificidade, mesmo conjunto de casamento), isso demonstra que o CSS é o mesmo. + +``` +bootstrap|2977→2966 raw|2965→2965 distinct|dropped=0 added=0 +… +zephyr |3069→3058 raw|3057→3057 distinct|dropped=0 added=0 +``` + +**TypeScript.** Zero erros novos contra a baseline de `main` (o `tsconfig.json` da raiz tem uma +incompatibilidade `module`/`moduleResolution` pré-existente com tsc 6, então a comparação roda com +`--ignoreConfig` e flags equivalentes, dos dois lados). + +**Screenshots.** Qualitativamente corretos — a paginação pinta a caixa azul do item ativo, o +"Previous" desabilitado fica cinza, a aba ativa mantém a caixa branca com borda. Numericamente o +harness não fecha **neste ambiente**: o Playwright do repo fixa `chromium_headless_shell-1217` e o +container só tem o 1194. Com o binário aliasado, um spinner (círculo, sem texto, sem classe de +estado, intocado pela mudança) já diverge 0,43% por anti-aliasing, e rotas com texto divergem 2–16% +porque as métricas de fonte mudam e o parágrafo requebra. Rodar o harness de verdade exige o binário +fixado. + +## Conserto pré-existente necessário para verificar + +`ve-project2` não buildava em `main`: `theme-runtime/theme-runtime.ts` ainda importava +`themes/{theme}/utilities/used/styles.css` para os 27 temas, e essa família foi retirada em `dd753ef4` +("retire utilities/used split"). O arquivo é gerado; `node scripts/generate-ve-theme/generate-granular-loaders.mjs` +regenera ele e os 27 `theme.ts`. Sem isso nenhum style-loader builda. diff --git a/docs/ve-architecture.md b/docs/ve-architecture.md index 2d8dedcaf..98e9476ab 100644 --- a/docs/ve-architecture.md +++ b/docs/ve-architecture.md @@ -271,6 +271,8 @@ ve-project2/src/ 17. **Theme-local selectors and resolved component values must come from `screenshots/{theme}/**/style.css`.** If a component needs a special-case rule beyond the global `theme.css` vars, derive it from the matching route-level `style.css` files for the same theme rather than from another theme or from package sources. 18. **Source CSS element rules (`h1`/`h4`/`p`/`hr`, etc.) must be migrated via `contents` contracts, never by targeting raw elements in selectors.** Create a contract class in `theme-contract/contents/contract.css.ts`, implement theme rules in `themes/{theme}/contents/styles.css.ts`, and stamp those classes directly on component elements (for example `

`). Do not use selectors like ``${scope} hr`` or ``${scope}${component} p``. 19. **Static string class selectors from source CSS (for example `.container`, `.container-fluid`) must not be authored inside `themes/{theme}/{family}/styles.css.ts`.** Migrate these rules through `theme-contract/contents/contract.css.ts` and implement them in `themes/{theme}/contents/styles.css.ts`, then apply the corresponding contents contract classes in markup. Do not write raw selector strings like `.container` or `.container-fluid` in component-family theme files. +20. **One contract per source class token — never a per-family alias for a shared state class.** `.dropdown-item.active` is two tokens and emits `${dropdownItem}${active}`; the composition happens at the call site. Do not add `dropdownItemActive`, `btnShowHook`, `navLinkDisabled` or anything shaped like them. `active`, `disabled`, `show`, `showing`, `fade`, `collapsed`, `collapsing` are owned by `theme-contract/literal/contract.css.ts` and by nothing else — a second copy in a family module shadows the live one depending on directory read order. Compound selectors already isolate components (`${modal}${show}` cannot match a dropdown), so a shared hash is safe. A genuine interference case gets a named entry in the divergence manifest, never a family-specific contract. +21. **Every zero-style contract must be styled by some theme.** An unwired contract compiles, exports a real hashed class and renders nothing — the failure is invisible until someone reports that an element "looks unstyled". `npm run check:contract-wiring` enforces this before a build and `build-contract.mjs` re-checks it against real hashes; deliberate inertness goes in `scripts/contract-wiring-allowlist.mjs` with a reason. --- @@ -293,6 +295,11 @@ export const myComponentVariant = style({}) All exports must be `style({})` — no properties. +One export per Bootstrap class token. A rule like `.my-component.active` does **not** get a +`myComponentActive` contract: `.active` is already owned by `theme-contract/literal/contract.css.ts`, +and the theme rule composes the two (`${myComponent}${active}`). See Core Rule 20 — this is the +mistake that left four state contracts exported and unwired for two releases. + Create `ve-project2/src/theme-contract/ui/{family}/_vars.css.ts` with a `createVar()` for each Bootstrap CSS custom property used by this family: ```ts diff --git a/docs/ve2-literal-conversion-plan.md b/docs/ve2-literal-conversion-plan.md index 992d689ea..3f6d3d65c 100644 --- a/docs/ve2-literal-conversion-plan.md +++ b/docs/ve2-literal-conversion-plan.md @@ -455,6 +455,8 @@ TypeScript check exits 0. > **T6 gap (discovered in T8):** The CSS styles for modal, accordion, and toast use *component-specific* contract symbols for their "show" state (`modalShowHook`, `accordionButtonCollapsed`, `toastFade`/`toastShow`) — NOT the shared literal `show`/`fade` — so the adapter/component changes in T6 caused those CSS rules to never fire. T8 reverted the three affected adapters to match their respective `styles.css.ts`. +> **T6 completed (T11).** The T8 revert fixed the symptom on the wrong side: it kept the per-family hooks and taught the emitter to produce them, via 20 `remapSymbols`/mirror entries in the divergence manifest. The CSS was never regenerated, so the contract layer kept both universes and the manifest became a hand-maintained sync list between two hand-maintained files. Nothing checked it, and 18 of the 33 per-family state aliases ended up exported with no rule in any theme — including the four in the `dropdownItemActive` report. T11 finishes §8.6 in the original direction: the aliases are deleted, the manifest drops to 2 entries (`body-split`, `legend-element-mirror`), the themes are regenerated onto `${component}${state}`, and the adapters pass the literal hashes. See T11 below. + --- **✅ T7 — Markup‑parity check (Phase 3).** Extend `diff-scenario-markup.mjs`/`markup-diff-core.mjs` to assert class+tag contract stamping and `@screenshot` annotations; emit gap reports. @@ -591,6 +593,31 @@ Biome `--write` applied across all modified dirs (11 files fixed, import sorting --- +**✅ T11 — Finish §8.6: retire the per‑family state aliases.** Delete every contract that is an alias for a shared Bootstrap state class, regenerate the 27 themes onto `${component}${state}`, point the adapters and components at the literal hashes, and gate the invariant so it cannot rot again. + +*Trigger:* the `dropdownItemActive` report — four contracts exported by the package, referenced by no theme, silently no‑op at the call site. The four were not an oversight; they were the unwired remainder of a whole category. + +*What the category was.* A per‑family alias collapses a compound source selector into one identifier: `.dropdown-item.active` → `dropdownItemActive`, `.btn.show` → `btnShowHook`, `.nav-link.active` → `navLinkActive`. 33 identifiers stood for 9 Bootstrap state classes. Because the literal emitter translates 1:1, each alias needed a manifest entry to exist in the CSS at all — 12 `remapSymbols` plus 8 mirror rules, hand‑written, one per family that happened to have a component demanding it. Families without a demo (dropdown item, list‑group item, breadcrumb, carousel, tab pane, …) never got an entry, so 18 of the 33 shipped with zero CSS. Nothing compared the two hand‑maintained lists. + +*Why deleting beats wiring.* Per‑family aliases are a pure rename: `${navLink}${navLinkActive}` and `${navLink}${active}` have identical specificity and identical match sets. They buy no isolation — the compound selector already provides it (`${modal}${show}` cannot match a dropdown) — while costing 20 manifest entries, 33 identifiers, and the drift that produced the bug. §8.6 said this in the original plan; T8 reverted it on the wrong side. + +*Changes.* +- Divergence manifest 22 → 2 entries. Only `body-split` (overlay bleed) and `legend-element-mirror` (component stamps the `legend` class) remain — both justified under §7.2, neither a naming accommodation. `remapSymbols` stays as a mechanism for a real, documented interference case. +- 36 contracts deleted: the 33 aliases, plus `tooltipVe`/`popoverVe` (templates already stamped the live `tooltip`/`popover` alongside them) and the duplicate `fade`/`show`/`collapse` copies in `ui/navs`, `ui/modal`, `ui/navbar` that shadowed the literal ones. `literal/` is now the single owner of every shared state token. +- Kept: `modalOpenHook` and `carouselSlide` — `.modal-open` and `.slide` have no rule in any theme's `bootstrap.css`, so they are runtime markers, not aliases of a literal contract. +- 27 themes regenerated: −4 290 lines of generated CSS, the per‑family `.fade` / `.fade:not(.show)` mirrors gone, one generic rule each. +- 8 adapters + ~80 components stamp the literal hashes. `ve-dropdown` passes one `show` for both `CLASS_NAME_SHOW_TRIGGER` and `CLASS_NAME_SHOW_MENU`; `ve-carousel` drops the double‑stamp workaround; `ve-toast` drops `CLASS_NAME_HIDE` (`.hide` has no rule to map). + +*Gate.* `scripts/check-contract-wiring.mjs` (static, pre‑build) plus a liveness assertion in `build-contract.mjs` (authoritative, real hashes vs emitted CSS). Deliberate inertness is declared in `scripts/contract-wiring-allowlist.mjs` with a reason; a state hook is never a valid entry. + +*Two further bugs the gate surfaced.* `build-contract.mjs` only checked liveness for duplicate candidates — a name declared by exactly one module was published unchecked, which is the precise mechanism that shipped the four. And `contractModules()` filtered by filename, excluding `layout/container.css.ts`, so `containerFluid` resolved to literal's dead copy while the themes styled layout's. + +*Result:* package dead exports 90 → 68, all 68 allowlisted (element markers, Bootstrap 3/4 names BS5 dropped, demo scaffolding, and inherited debt tagged TRIAGE). `active` keeps hash `b17c3vgbe3`, so consumers already composing with it are unaffected. + +*Breaking:* `cx(dropdownItem, isActive && dropdownItemActive)` becomes `cx(dropdownItem, isActive && active)`, with `active` from the package's `global` entry. The removed names now fail to compile instead of failing to paint. + +--- + ## 13. File inventory **Create** diff --git a/docs/ve2-theme-generator-audit-playbook.md b/docs/ve2-theme-generator-audit-playbook.md index f441e83ee..7a71dfef2 100644 --- a/docs/ve2-theme-generator-audit-playbook.md +++ b/docs/ve2-theme-generator-audit-playbook.md @@ -205,7 +205,7 @@ Log (append-only): `docs/ve-theme-generator-validation/family-audit/verify-brite | Nav tab panel contracts | `ui/navs/contract.css.ts` + `css-utils.mjs` | `tabContent`/`tabPane`/`fade`/`show`/`navButtonReset`; `.fade:not(.show)` uses navs `show` contract (not `modalShowHook`) | | Progress bar color variants | `css-utils.mjs` → `FAMILY_CLASS_TO_CONTRACT['ui/progress']` | `.bg-success`/`.bg-info`/`.bg-warning`/`.bg-danger` → `progressBar*`; `.text-dark` → `progressBarTextDark` | | No global element selectors in family CSS | `rule-transpiler.mjs` + `css-source.mjs` | `isScopedFamilySelector()` skips unmapped raw selectors; element bleed routed to owner family | -| No literal Bootstrap state classes in family CSS | `css-utils.mjs` + `rule-transpiler.mjs` | Never emit `.show`, `.disabled`, `.active`, `.collapsed` as string literals — map to contract hooks (`tooltipShow`, `btnShowHook`, `btnDisabledHook`, `navLinkDisabled`, `pageItemDisabled`, `dropdownItemDisabled`, `listGroupItemActive`, …). Pagination orphan parents (`.active > .page-link`) use `ORPHAN_STATE_SEGMENT_BY_FAMILY`. Wire Bootstrap JS via `CLASS_NAME_SHOW` bridges where applicable. | +| ~~No literal Bootstrap state classes in family CSS~~ | ~~`css-utils.mjs` + `rule-transpiler.mjs`~~ | **Reversed — do not follow.** This rule told the v1 generator to collapse compound state selectors into per-family hooks (`btnShowHook`, `navLinkDisabled`, `dropdownItemDisabled`, `listGroupItemActive`, …). One hook per family per state produced 33 identifiers for 9 Bootstrap classes, wired by a hand-maintained remap list that nobody kept in sync — 18 of the 33 were exported and styled by no theme. The v2 rule is the opposite: **a shared state class is one class token and maps 1:1 to its literal contract**, so `.dropdown-item.active` emits `${dropdownItem}${active}` and the composition happens at the call site. See [§8.6](./ve2-literal-conversion-plan.md) and `scripts/check-contract-wiring.mjs`. | Audit logs: [`docs/ve-theme-generator-validation/family-audit/`](../docs/ve-theme-generator-validation/family-audit/) diff --git a/package.json b/package.json index 6df0e0772..ecbc888ee 100755 --- a/package.json +++ b/package.json @@ -24,10 +24,11 @@ "generate:ve-theme": "node scripts/generate-ve-theme.mjs", "generate:ve-literal": "node scripts/generate-ve-literal.mjs", "pkg:generate": "node scripts/generate-ve-literal.mjs --all-themes --families", + "check:contract-wiring": "node scripts/check-contract-wiring.mjs", "pkg:build": "node scripts/build-package.mjs", "pkg:pack": "node scripts/pack-dist.mjs", "pkg:probe": "node scripts/preset-probe.mjs", - "pkg:ci": "npm run pkg:build && npm run pkg:contract && npm run pkg:pack", + "pkg:ci": "npm run check:contract-wiring && npm run pkg:build && npm run pkg:contract && npm run pkg:pack", "pkg:release": "node scripts/release.mjs", "format": "biome format --write", "lint": "biome lint --write", diff --git a/scripts/_cssdiff.mjs b/scripts/_cssdiff.mjs new file mode 100644 index 0000000000000000000000000000000000000000..45fd8b3004e7cbd8a49c2f1aadab93983d9aa406 GIT binary patch literal 3699 zcmai1-EQN?70#{n6eoZHl8#L6ZkwVo;4JLeK^DkjgZ17?YBVB8(nRD8J2Ui86#}{H z0~CFO0_}t3P4XoD&I~2XcG6zhBxmM)bAG>bG&wmL(g|JvwNqQdB2JJ|8SxXv4c8(AlzOB|>hVS8vi+sZ4wCfimpTY8t%Cpch_ekZM@ zro}%iwWwJ)XBbtia35)7YaDgKKS2+FXQB~Qx6Tu+DXdz`u;&0kU*FuoGyf!cU&zKE zJYFknjb&uk1lQbcRI$M+<=A0H!(A}d%0>!Lf`Ttq5%q3$31vj@)>GHWLangL8ne+1 z3wKxvFCf&Wszl=?Dcw774`#IFAWw#>ZjAMap}d#HH>#3154xZ(hBDEnlxM|8Rpk$j zHHCC8d6ckHl%;~R`GrNFimluPGPQE@QK%7!e8`dr&ELszh)}|VmlrqJbWZ(6xv=Zo z>6lS+*F1~wraRY(faG=HQcS>(B3a@VE86_wLyd>kp&K0s@S;-Q6W&g^%jabWf zUe>=MxZnC6Nl_HWmZ-nW)_bGB?xV*S>?2w74c-XnZ#L!*8xD^rYe?~a8E=Q3Zv+BK z$qEl%t4)bIhUmA(Y>!aF`m?hrDiluh;=Zu@>}+Ei1FCp-wqJdAcIowRXi!?yl;%#q zm%4ol)AjKW1^ZDVNLt>}N4dVfZ_>qVHk!?L>1g+1$x+U?^1-Dcnpys%Q94b2O0q7S z&AP>yl68^|hm#XfM$cmO>)-!@dbeDGr7S8DiIBX!_v28ykphnx5JiPT48)dmC9PVG z3^6*Eb?QuO3z=~y#q>6mLppu^I;b1bMr82cVRcp|cqzdIeajk1qp-k&0+FUq?y>}X}0uefB z>CQ+Y)oKBADKP3U|M}%#hDj6kyq5Pl69TIVdO~TZX$?KCjLW@o8GU|^Ftk+_?CcFa z`1G9W>Faq8CaXjt(>`!xN`C-BXLSA*EoP(LXcxjX74xoJ;JzM(qu_954GzHKAa@Oz zKTT#DvE%O=UQBT@V5;0(Ri|0pSg8t(on9hurOG0 z0~@bal*(MWD`i9Pet^qZ+q`MrCe8CajkoryHd*kfthj>&(a>hj5%a|H5a9B{x!k+t z2&H-MioB0U+Jsnr-sDObRf~R^!sS6%C!4Ns>qje#EX(`2!JE-F%h=dr?Z-Nb8?j3t zuOB%S8XWnww~kvwbluU(>2^$d&i)V0x%bDu!KDlY`e^;?&k39pXM>m=NR6OFOpeFY z-~{A4<=F;)OF-WkrN9bztF^6}z+1j;}tH(quN9tf9yRoVJ)F zGdQy`-I+4de^3xSKN!^F{<2-Iq&=fQ{Sm{#>sN2~58m%MT2u&+34}q|nJAmQGV63P zWrF2<=M?V`^@%+H9}a_2@JkHS!7=@3D4QpqImDHxp38k~r^v)!B+z&^KjhFh=1}zX zM!0l~6pb0c5Eo%qJ10hnXMP8uhkuITTIRU44tFp_onvTjk*LtX%CZHCrD9CWa6q8$ zmt)j_%*}(>!e{}9#mOQ}fX|2E3Pcjv5X0$yutej*jP_!<1tUI*2?05#1iwM!G@6oi z`*Cu#>y2~jZ?YqdCK!7Q{}O0vg)TN{Jxp-dF-r8`{{z{>$J>e9cf%vmX!jV>96vnP ze#6=G%$8@;m?v45EjvW>NVpHQ!`#Ags>D)OW8w&hfFOUFrTDuSBBm-3bmZmOaSw5t z>CrAggseVF=`ELkKyj5q$X|`=&6klW`rb4}qZo%d!Ofw`cHf&mJ2aKUEg?#>>Sh{d z1fq{jd09{-N_&lN5ezjdsUYp5j` presentes no CSS emitido (classes `.b…` e vars `--b…`). Basta -// um tema — os hashes são theme-agnostic. +// tokens `b` presentes no CSS emitido (classes `.b…` e vars `--b…`). +// União dos 27 temas: o hash é theme-agnostic, mas a EXISTÊNCIA de uma regra não é — +// dezenas de classes legadas do BS3/4 (`.help-block`, `.card-primary`, `.pager`) só +// aparecem no CSS de alguns Bootswatch. Ler só `bootstrap` as marcaria como mortas. async function liveHashes() { - const themeDir = existsSync(path.join(THEMES, 'bootstrap')) - ? path.join(THEMES, 'bootstrap') - : null - if (!themeDir) throw new Error('dist-pkg/themes/bootstrap ausente — rode pkg:build antes') + if (!existsSync(THEMES)) throw new Error('dist-pkg/themes ausente — rode pkg:build antes') const live = new Set() - for (const f of (await readdir(themeDir)).filter((f) => f.endsWith('.css'))) { - const css = await readFile(path.join(themeDir, f), 'utf8') - for (const m of css.matchAll(/(?:\.|--)(b[a-z0-9]+)\b/g)) live.add(m[1]) + for (const theme of await readdir(THEMES)) { + const themeDir = path.join(THEMES, theme) + for (const f of (await readdir(themeDir)).filter((f) => f.endsWith('.css'))) { + const css = await readFile(path.join(themeDir, f), 'utf8') + for (const m of css.matchAll(/(?:\.|--)(b[a-z0-9]+)\b/g)) live.add(m[1]) + } } return live } @@ -145,12 +149,23 @@ async function main() { const varsByEntry = {} let unresolvedFamily = 0 let deadDup = 0 + // Candidata ÚNICA nunca passava por `live` — era esse buraco que publicava + // `dropdownItemActive` & cia.: um hash real, exportado, sem uma única regra no + // CSS de nenhum dos 27 temas. Aplicar a classe era um no-op silencioso. + const deadSingles = [] for (const [name, cands] of nameEntries) { const chosen = resolve(cands) if (!chosen) { deadDup++ continue } + if ( + !isVar(chosen.value) && + !live.has(innerHash(chosen.value) ?? '') && + !UNWIRED_ALLOWLIST[name] + ) { + deadSingles.push(name) + } if (isVar(chosen.value)) { const entry = moduleToEntry(chosen.module) if (!varsByEntry[entry]) varsByEntry[entry] = new Map() @@ -208,6 +223,17 @@ async function main() { console.log( ` sem família (skipped): ${unresolvedFamily}; duplicatas mortas (skipped): ${deadDup}`, ) + + if (deadSingles.length > 0) { + console.error( + `\nERRO: ${deadSingles.length} contract(s) exportado(s) sem UMA regra no CSS de nenhum tema.\n` + + 'Aplicá-los é um no-op silencioso: classe válida no DOM, zero CSS por trás.\n' + + 'Ou remova o contract, ou ligue-o num tema, ou — se for inerte de propósito —\n' + + 'declare-o em scripts/contract-wiring-allowlist.mjs com o motivo.\n', + ) + for (const name of deadSingles.sort()) console.error(` ${name}`) + process.exit(1) + } } main().catch((e) => { diff --git a/scripts/check-contract-wiring.mjs b/scripts/check-contract-wiring.mjs new file mode 100644 index 000000000..9a8550d92 --- /dev/null +++ b/scripts/check-contract-wiring.mjs @@ -0,0 +1,215 @@ +#!/usr/bin/env node +/** + * Contract wiring gate — static pre-build check. + * + * A contract export is a zero-style `style({})` identifier. It is only useful if + * some theme writes CSS for it. Nothing in the type system says so: an unwired + * contract compiles, exports a real hashed class, and renders nothing. + * + * Contract NAMES are declared more than once on purpose — `literal/contract.css.ts` + * is the full 1:1 registry output and the per-family modules re-declare the subset + * they own, so most names have two hashes and only one is styled. `build-contract.mjs` + * resolves that by picking the candidate with a live rule. So the question a gate must + * ask is not "is this pair wired" but "is ANY copy of this name wired" — a name with + * no live copy anywhere is what reaches consumers as a silent no-op. + * + * 1. dead name — no copy referenced by any family chunk. Ships, styles nothing. + * 2. monolith-only — styled by themes/{t}/literal/ but not by the family chunks the + * package is built from, so it is dead for consumers. + * 3. closure — a source CSS class whose registry symbol no module declares. + * + * The authoritative version of check 1 runs inside build-contract.mjs against real + * hashes and real CSS; this one is the fast approximation you can run before a build. + * + * Usage: + * node scripts/check-contract-wiring.mjs # gate: exit 1 on failure + * node scripts/check-contract-wiring.mjs --report # inventory, always exit 0 + * node scripts/check-contract-wiring.mjs --no-closure + */ + +import { readdir, readFile } from 'node:fs/promises' +import path from 'node:path' +import process from 'node:process' + +import { UNWIRED_ALLOWLIST } from './contract-wiring-allowlist.mjs' +import { VE2_CONTRACT_ROOT, VE2_THEMES_ROOT } from './generate-ve-theme/constants.mjs' + +const STYLE_EXPORT = /export const (\w+) = style\(\{(\}?)\)?/g +const IMPORT_BLOCK = /import\s*\{([^}]+)\}\s*from\s*'([^']+)'/g +const CONTRACT_REF = /\$\{(\w+)\}/g + +async function collect(dir, suffix) { + const out = [] + for (const entry of await readdir(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name) + if (entry.isDirectory()) out.push(...(await collect(full, suffix))) + else if (entry.name.endsWith(suffix)) out.push(full) + } + return out +} + +/** + * Contract name → the modules that declare it. + * + * `zeroStyleOnly` (the gate's view) keeps only `style({})` identifiers. A contract + * declared with real properties carries its own CSS and needs no theme rule, so it + * can never be "unwired"; callers that resolve imports want those too. + */ +export async function readContracts({ zeroStyleOnly = true } = {}) { + const bySymbol = new Map() + for (const file of await collect(VE2_CONTRACT_ROOT, '.css.ts')) { + const mod = path.relative(VE2_CONTRACT_ROOT, file).replace(/\\/g, '/').replace(/\.ts$/, '') + const src = (await readFile(file, 'utf8')).replace(/\r/g, '') + STYLE_EXPORT.lastIndex = 0 + let m + // biome-ignore lint/suspicious/noAssignInExpressions: idioma de iteração com regex.exec() + while ((m = STYLE_EXPORT.exec(src)) !== null) { + if (zeroStyleOnly && m[2] !== '}') continue + if (!bySymbol.has(m[1])) bySymbol.set(m[1], new Set()) + bySymbol.get(m[1]).add(mod) + } + } + return bySymbol +} + +/** Contract names a generated theme file both imports and interpolates into a selector. */ +function symbolsStyledIn(src) { + const imported = new Set() + IMPORT_BLOCK.lastIndex = 0 + let m + // biome-ignore lint/suspicious/noAssignInExpressions: idioma de iteração com regex.exec() + while ((m = IMPORT_BLOCK.exec(src)) !== null) { + if (!m[2].includes('theme-contract/')) continue + for (const raw of m[1].split(',')) { + const name = raw + .trim() + .split(/\s+as\s+/) + .pop() + if (name) imported.add(name) + } + } + + const styled = new Set() + CONTRACT_REF.lastIndex = 0 + // biome-ignore lint/suspicious/noAssignInExpressions: idioma de iteração com regex.exec() + while ((m = CONTRACT_REF.exec(src)) !== null) { + if (imported.has(m[1])) styled.add(m[1]) + } + return styled +} + +/** + * The package is built from the per-family chunks; `themes/{t}/literal/styles.css.ts` + * is an alternative monolith loader the package excludes, so track the two separately. + */ +async function readThemeUsage() { + const families = new Set() + const monolith = new Set() + const themes = (await readdir(VE2_THEMES_ROOT, { withFileTypes: true })) + .filter((e) => e.isDirectory()) + .map((e) => e.name) + + for (const theme of themes) { + for (const file of await collect(path.join(VE2_THEMES_ROOT, theme), '.css.ts')) { + const rel = path.relative(VE2_THEMES_ROOT, file).replace(/\\/g, '/') + const target = rel.includes('/literal/') ? monolith : families + const src = (await readFile(file, 'utf8')).replace(/\r/g, '') + for (const symbol of symbolsStyledIn(src)) target.add(symbol) + } + } + return { families, monolith, themeCount: themes.length } +} + +async function checkClosure(bySymbol) { + const { buildLiteralRegistry } = await import('./generate-ve-literal/registry.mjs') + const registry = await buildLiteralRegistry() + const missing = [] + for (const [cssClass, symbol] of registry.classMap) { + if (!bySymbol.has(symbol)) missing.push({ cssClass, symbol }) + } + return missing.sort((a, b) => a.cssClass.localeCompare(b.cssClass)) +} + +function printGroup(title, blurb, rows, format) { + if (rows.length === 0) return + console.log(`\n${title} — ${rows.length}`) + console.log(` ${blurb}`) + for (const row of rows) console.log(` ${format(row)}`) +} + +async function main() { + const argv = process.argv.slice(2) + const reportOnly = argv.includes('--report') + const skipClosure = argv.includes('--no-closure') + + const bySymbol = await readContracts() + const { families, monolith, themeCount } = await readThemeUsage() + + const dead = [] + const monolithOnly = [] + const stale = [] + for (const [symbol, modules] of bySymbol) { + const allowed = Boolean(UNWIRED_ALLOWLIST[symbol]) + if (families.has(symbol)) { + if (allowed) stale.push(symbol) + continue + } + if (allowed) continue + const row = { symbol, modules: [...modules].sort().join(', ') } + if (monolith.has(symbol)) monolithOnly.push(row) + else dead.push(row) + } + const bySymbolName = (a, b) => a.symbol.localeCompare(b.symbol) + dead.sort(bySymbolName) + monolithOnly.sort(bySymbolName) + + const missing = skipClosure ? [] : await checkClosure(bySymbol) + + console.log( + `contract wiring: ${bySymbol.size} contract names, ${themeCount} themes, ` + + `${Object.keys(UNWIRED_ALLOWLIST).length} allowlisted`, + ) + + printGroup( + '[1] dead contract', + 'Exported and hashed, but no family chunk in any theme styles it. Applying it is a no-op.', + dead, + ({ symbol, modules }) => `${symbol.padEnd(28)} ${modules}`, + ) + + printGroup( + '[2] monolith-only contract', + 'Styled by themes/{t}/literal/ but not by the family chunks the package is built from.', + monolithOnly, + ({ symbol, modules }) => `${symbol.padEnd(28)} ${modules}`, + ) + + printGroup( + '[2b] stale allowlist entry', + 'Now styled by a family chunk; drop it from the allowlist.', + stale.sort(), + (symbol) => symbol, + ) + + printGroup( + '[3] registry closure gap', + 'Source CSS class whose registry symbol no contract module declares.', + missing, + ({ cssClass, symbol }) => `.${cssClass.padEnd(40)} → ${symbol}`, + ) + + const failures = dead.length + monolithOnly.length + stale.length + missing.length + if (failures === 0) { + console.log('\n✔ every contract is styled by some theme and the registry is closed') + process.exit(0) + } + console.log(`\n${failures} problem(s)`) + process.exit(reportOnly ? 0 : 1) +} + +if (process.argv[1]?.endsWith('check-contract-wiring.mjs')) { + main().catch((err) => { + console.error(err) + process.exit(1) + }) +} diff --git a/scripts/contract-wiring-allowlist.mjs b/scripts/contract-wiring-allowlist.mjs new file mode 100644 index 000000000..6daa1780b --- /dev/null +++ b/scripts/contract-wiring-allowlist.mjs @@ -0,0 +1,116 @@ +/** + * Contracts that are deliberately unwired — no theme writes CSS for them. + * Consumed by scripts/check-contract-wiring.mjs. Every entry needs a reason. + * + * A state hook does NOT belong here. Shared Bootstrap state classes map 1:1 to the + * literal contracts (`active`, `disabled`, `show`, `fade`, `collapsed`, …) and compose + * at the call site — see docs/ve2-literal-conversion-plan.md §8.6. Adding + * `dropdownItemActive` here instead of deleting it would re-create the bug the gate + * exists to prevent: a name that type-checks, ships a hash, and styles nothing. + * + * Entries tagged TRIAGE are inherited debt: unwired, plausibly intentional, not yet + * confirmed by anyone who knows the original intent. Resolve them by either deleting + * the contract or wiring it — not by upgrading the tag. + */ + +const ELEMENT_MARKER = + 'Element marker: stamped so descendant/combinator rules can target the element. ' + + 'Carries no base style of its own.' +const BOOTSTRAP_LEGACY = + 'Bootstrap 3/4 class name that Bootstrap 5 dropped (BS5 uses .text-bg-* / .text-*). ' + + 'Kept so the published API does not break for consumers still passing it.' +const RUNTIME_MARKER = + 'Runtime-only marker: Bootstrap JS toggles it, no theme stylesheet has a rule for it.' +const APP_SCAFFOLDING = + 'Demo-page scaffolding, not a Bootstrap class. Styled by the docs app, not by a theme.' +const TRIAGE = 'TRIAGE — unwired, intent unconfirmed.' + +/** @type {Record} */ +export const UNWIRED_ALLOWLIST = { + // ── Element markers ────────────────────────────────────────────────────────── + elBlockquote: ELEMENT_MARKER, + elCite: ELEMENT_MARKER, + elDataBsTarget: ELEMENT_MARKER, + elDiv: ELEMENT_MARKER, + elEm: ELEMENT_MARKER, + elFieldset: ELEMENT_MARKER, + elFigcaption: ELEMENT_MARKER, + elForm: ELEMENT_MARKER, + elHgroup: ELEMENT_MARKER, + elHidden: ELEMENT_MARKER, + elI: ELEMENT_MARKER, + elMain: ELEMENT_MARKER, + elMark: ELEMENT_MARKER, + elNav: ELEMENT_MARKER, + elReadonly: ELEMENT_MARKER, + elS: ELEMENT_MARKER, + elSection: ELEMENT_MARKER, + elTable: ELEMENT_MARKER, + elTbody: ELEMENT_MARKER, + elTd: ELEMENT_MARKER, + elTfoot: ELEMENT_MARKER, + elTh: ELEMENT_MARKER, + elThead: ELEMENT_MARKER, + elTr: ELEMENT_MARKER, + + // ── Bootstrap 3/4 names Bootstrap 5 dropped ────────────────────────────────── + badgeDanger: BOOTSTRAP_LEGACY, + badgeDark: BOOTSTRAP_LEGACY, + badgeLight: BOOTSTRAP_LEGACY, + badgePrimary: BOOTSTRAP_LEGACY, + badgeRoundedPill: BOOTSTRAP_LEGACY, + badgeSecondary: BOOTSTRAP_LEGACY, + badgeSuccess: BOOTSTRAP_LEGACY, + badgeTextDark: BOOTSTRAP_LEGACY, + progressBarDanger: BOOTSTRAP_LEGACY, + progressBarInfo: BOOTSTRAP_LEGACY, + progressBarSuccess: BOOTSTRAP_LEGACY, + progressBarTextDark: BOOTSTRAP_LEGACY, + progressBarWarning: BOOTSTRAP_LEGACY, + spinnerDanger: BOOTSTRAP_LEGACY, + spinnerDark: BOOTSTRAP_LEGACY, + spinnerInfo: BOOTSTRAP_LEGACY, + spinnerLight: BOOTSTRAP_LEGACY, + spinnerPrimary: BOOTSTRAP_LEGACY, + spinnerSecondary: BOOTSTRAP_LEGACY, + spinnerSuccess: BOOTSTRAP_LEGACY, + spinnerWarning: BOOTSTRAP_LEGACY, + + // ── Runtime-only markers ───────────────────────────────────────────────────── + carouselSlide: + `${RUNTIME_MARKER} Bootstrap's CLASS_NAME_SLIDE; \`.slide\` has no rule in any theme's bootstrap.css.`, + + // ── Split by the divergence manifest ───────────────────────────────────────── + body: + 'Split into bodyFrame (canvas) + bodyText (typography) by the body-split divergence, ' + + 'so the full `body` contract is intentionally never emitted.', + + // ── Demo-page scaffolding ──────────────────────────────────────────────────── + actionsRow: APP_SCAFFOLDING, + bdPlaceholderImg: APP_SCAFFOLDING, + bdPlaceholderImgLg: APP_SCAFFOLDING, + cardHorizontalColBody: APP_SCAFFOLDING, + cardHorizontalColImg: APP_SCAFFOLDING, + cardHorizontalRow: APP_SCAFFOLDING, + placeholderIcon: APP_SCAFFOLDING, + toastBrand: APP_SCAFFOLDING, + toastExample: APP_SCAFFOLDING, + toastRuntime: APP_SCAFFOLDING, + toastTimestamp: APP_SCAFFOLDING, + + // ── Inherited debt ─────────────────────────────────────────────────────────── + alertBtnClose: `${TRIAGE} Maps .btn-close; literal owns btnClose.`, + btnGroupButton: TRIAGE, + btnGroupInteractive: TRIAGE, + floatingLabel: TRIAGE, + form: TRIAGE, + formControlFloating: TRIAGE, + horizontalRuleCard: TRIAGE, + inputFontFamily: `${TRIAGE} Element-group contract superseded by el* reboot rules.`, + marginEnd2: `${TRIAGE} Maps .me-2; literal owns me2.`, + mediaMiddle: TRIAGE, + navButtonReset: TRIAGE, + navbarBgLight: `${TRIAGE} Absorbs \`navbar-light bg-light\`.`, + navbarDarkBgPrimary: `${TRIAGE} Absorbs \`navbar-dark bg-primary\`.`, + rowCol: TRIAGE, +} diff --git a/scripts/generate-ve-literal/divergence-manifest.mjs b/scripts/generate-ve-literal/divergence-manifest.mjs index 5f02b443b..d01a4c4ba 100644 --- a/scripts/generate-ve-literal/divergence-manifest.mjs +++ b/scripts/generate-ve-literal/divergence-manifest.mjs @@ -66,210 +66,6 @@ export const divergences = [ 'carry ${theme} ${vars} ${bodyText} explicitly so --bs-* vars resolve outside the scope tree.', }, - // ---- JS-adapter hook remaps ---- - - { - id: 'dropdown-menu-show', - match: { selector: '.dropdown-menu.show' }, - action: 'overrideSelector', - contracts: ['dropdownMenu', 'dropdownMenuShow'], - reason: - 'VE Bootstrap JS (ve-dropdown.ts) stamps dropdownMenuShow on the visible menu, ' + - 'not the literal show contract. The source .dropdown-menu.show rule must target ' + - 'dropdownMenuShow so the menu becomes visible when the dropdown opens.', - }, - - { - id: 'btn-show-hook', - match: { selectorContains: '.btn.show' }, - action: 'addMirrorRule', - substitute: { fromContract: 'show', toContract: 'btnShowHook' }, - reason: - 'VE Bootstrap JS (ve-dropdown.ts CLASS_NAME_SHOW_TRIGGER=btnShowHook) stamps ' + - 'btnShowHook on the trigger button when a dropdown opens, not the literal show ' + - 'contract. Mirror every .btn.show rule with btnShowHook so trigger-button active ' + - 'styling fires.', - }, - - // ---- Component-specific state contract remaps ---- - // These rules use shared Bootstrap state classes (.active, .disabled, .show, etc.) - // but the VE components stamp component-specific contracts. The CSS must use those - // same contracts so the rules fire when the JS/TSX stamps the correct class hash. - - { - id: 'nav-link-active', - match: { selectorContains: '.nav-link.active' }, - action: 'remapSymbols', - symbolMap: { active: 'navLinkActive' }, - reason: - 'BasicNav/PillNav/NavbarNav stamp navLinkActive on the active link, not the ' + - 'shared active contract. Without this remap the active-tab/pill CSS never fires.', - }, - { - id: 'nav-link-disabled', - match: { selectorContains: '.nav-link.disabled' }, - action: 'remapSymbols', - symbolMap: { disabled: 'navLinkDisabled' }, - reason: - 'Nav components stamp navLinkDisabled for disabled links; the shared disabled ' + - 'contract is not stamped by the nav adapter.', - }, - { - id: 'accordion-button-not-collapsed', - match: { selectorContains: '.accordion-button:not' }, - action: 'remapSymbols', - symbolMap: { collapsed: 'accordionButtonCollapsed' }, - reason: - 've-accordion stamps accordionButtonCollapsed; .accordion-button:not(.collapsed) ' + - 'must target that contract so the active-accordion styles fire.', - }, - { - id: 'accordion-last-item-collapsed', - match: { selectorContains: '.accordion-item:last-of-type' }, - action: 'remapSymbols', - symbolMap: { collapsed: 'accordionButtonCollapsed' }, - reason: - 'The last accordion-item border-radius rule uses .accordion-button.collapsed; ' + - 'must target accordionButtonCollapsed to match what ve-accordion stamps.', - }, - { - id: 'toast-show-hidden', - match: { selectorContains: '.toast:not(.show)' }, - action: 'remapSymbols', - symbolMap: { show: 'toastShow' }, - reason: - 've-toast stamps toastShow when visible; the CSS :not() guard must use toastShow ' + - 'so hidden toasts get display:none correctly.', - }, - { - id: 'toast-showing', - match: { selectorContains: '.toast.showing' }, - action: 'remapSymbols', - symbolMap: { showing: 'toastShowing' }, - reason: 've-toast stamps toastShowing during the show transition, not the shared showing contract.', - }, - { - id: 'modal-show', - match: { selectorContains: '.modal.show' }, - action: 'remapSymbols', - symbolMap: { show: 'modalShowHook' }, - reason: - 've-modal CLASS_NAME_SHOW stamps modalShowHook, not the shared show contract. ' + - 'The .modal.show .modal-dialog rule must use modalShowHook for the open modal to ' + - 'have transform:none applied.', - }, - { - id: 'modal-backdrop-show', - match: { selectorContains: '.modal-backdrop.show' }, - action: 'remapSymbols', - symbolMap: { show: 'modalShowHook' }, - reason: - 'The modal backdrop uses the same CLASS_NAME_SHOW=modalShowHook as the modal itself. ' + - 'Without the remap the backdrop stays at opacity:0 when the modal opens.', - }, - { - id: 'pagination-active', - match: { selectorContains: '.page-' }, - action: 'remapSymbols', - symbolMap: { active: 'pageItemActive', disabled: 'pageItemDisabled' }, - reason: - 've-pagination stamps pageItemActive/pageItemDisabled; the shared active/disabled ' + - 'contracts are not stamped by the pagination adapter.', - }, - - // ---- Tooltip family-specific show ---- - { - id: 'tooltip-show', - match: { selectorContains: '.tooltip.show' }, - action: 'remapSymbols', - symbolMap: { show: 'tooltipShow' }, - reason: - 've-tooltip stamps tooltipShow (CLASS_NAME_SHOW=tooltipShow), not the shared show ' + - 'contract. .tooltip.show opacity rule must target tooltipShow.', - }, - - // ---- Modal family-specific fade ---- - { - id: 'modal-fade-selector', - match: { selectorContains: '.modal.fade' }, - action: 'remapSymbols', - symbolMap: { fade: 'modalFade' }, - reason: - 've-modal uses CLASS_NAME_FADE=modalFade (not navs fade). ' + - '.modal.fade .modal-dialog transform rule must target modalFade.', - }, - { - id: 'modal-backdrop-fade', - match: { selectorContains: '.modal-backdrop.fade' }, - action: 'remapSymbols', - symbolMap: { fade: 'modalFade' }, - reason: 'Modal backdrop Backdrop class also receives CLASS_NAME_FADE=modalFade.', - }, - - // ---- Per-family .fade transition mirrors ---- - { - id: 'tooltip-fade-transition', - match: { selector: '.fade' }, - action: 'addMirrorRule', - substitute: { fromContract: 'fade', toContract: 'tooltipFade' }, - reason: 'Tooltip template uses tooltipFade; mirror .fade transition rule so tooltipFade elements animate.', - }, - { - id: 'popover-fade-transition', - match: { selector: '.fade' }, - action: 'addMirrorRule', - substitute: { fromContract: 'fade', toContract: 'popoverFade' }, - reason: 'Popover template uses popoverFade; mirror .fade transition rule so popoverFade elements animate.', - }, - { - id: 'modal-fade-transition', - match: { selector: '.fade' }, - action: 'addMirrorRule', - substitute: { fromContract: 'fade', toContract: 'modalFade' }, - reason: 've-modal template uses modalFade; mirror .fade transition rule so modalFade elements animate.', - }, - - // ---- Per-family .fade:not(.show) opacity mirrors ---- - // Each JS-driven family uses a family-specific fade+show pair instead of the shared - // navs fade/show. The generic .fade:not(.show) opacity rule uses navs symbols, so - // it never fires for these families. Mirror the rule for each family's symbols. - { - id: 'tooltip-fade-not-show', - match: { selector: '.fade:not(.show)' }, - action: 'addMirrorRule', - substitutes: [ - { fromContract: 'fade', toContract: 'tooltipFade' }, - { fromContract: 'show', toContract: 'tooltipShow' }, - ], - reason: - 'Tooltip uses tooltipFade+tooltipShow; mirror .fade:not(.show) opacity:0 rule ' + - 'so hidden tooltips are invisible before show is stamped.', - }, - { - id: 'popover-fade-not-show', - match: { selector: '.fade:not(.show)' }, - action: 'addMirrorRule', - substitutes: [ - { fromContract: 'fade', toContract: 'popoverFade' }, - { fromContract: 'show', toContract: 'popoverShow' }, - ], - reason: - 'Popover uses popoverFade+popoverShow; mirror .fade:not(.show) opacity:0 rule ' + - 'so hidden popovers are invisible before show is stamped.', - }, - { - id: 'modal-fade-not-show', - match: { selector: '.fade:not(.show)' }, - action: 'addMirrorRule', - substitutes: [ - { fromContract: 'fade', toContract: 'modalFade' }, - { fromContract: 'show', toContract: 'modalShowHook' }, - ], - reason: - 've-modal uses modalFade+modalShowHook; mirror .fade:not(.show) opacity:0 rule ' + - 'so the modal is invisible until modalShowHook is stamped.', - }, - // ---- element rules → legend class contract ---- // RadioButtons/DisabledRadioButtons stamp the `legend` class contract on , // not the `elLegend` element contract, so the bare `legend {…}` element rules (which @@ -301,7 +97,7 @@ export const divergences = [ // Concrete case (lumen only): the legacy `.pagination > li > a { color:#555 }` (source // specificity 0,1,2) becomes `${scope}${pagination} > ${scope}${elLi} > ${scope}${link}` // = 0,6,0 in VE, which OUTRANKS `.disabled > .page-link { color:#999 }` -// (`${scope}${pageItemDisabled} > ${scope}${pageLink}` = 0,4,0) — so the disabled Previous +// (`${scope}${disabled} > ${scope}${pageLink}` = 0,4,0) — so the disabled Previous // link rendered #555 instead of #999. In the source the disabled rule (0,3,0) beat the // legacy rule (0,1,2), so #999 won. // diff --git a/scripts/generate-ve-literal/emit.mjs b/scripts/generate-ve-literal/emit.mjs index d3a6a3a47..1d0f7daf4 100644 --- a/scripts/generate-ve-literal/emit.mjs +++ b/scripts/generate-ve-literal/emit.mjs @@ -915,7 +915,9 @@ export async function emitLiteralStyles(theme, opts = {}) { } // Apply remapSymbols divergences: substitute component-specific contracts for - // shared state contracts (e.g. active→navLinkActive) in matching selectors. + // shared ones in matching selectors. No entry uses this today — shared Bootstrap + // state classes map 1:1 to the literal contracts (plan §8.6). The hook stays for + // a genuine, documented interference case. let veSelector = rawVeSelector for (const remap of findRemaps(selector)) { for (const [from, to] of Object.entries(remap.symbolMap)) { diff --git a/scripts/generate-ve-theme/css-utils.mjs b/scripts/generate-ve-theme/css-utils.mjs index 8aa5c6f01..b1f38096d 100644 --- a/scripts/generate-ve-theme/css-utils.mjs +++ b/scripts/generate-ve-theme/css-utils.mjs @@ -56,14 +56,15 @@ export function camelToKebab(value) { return value.replace(/([A-Z])/g, '-$1').toLowerCase() } -/** Contract exports that do not map to a simple CSS class selector. */ +/** + * Contract exports that do not map to a simple CSS class selector. + * + * Compound Bootstrap state selectors do NOT belong here. `.btn.show` is two class + * tokens and translates to two contracts, `${btn}${show}` — collapsing it into one + * `btnShowHook` identifier is what left four state contracts exported and unwired. + * See docs/ve2-literal-conversion-plan.md §8.6. + */ export const CONTRACT_SELECTOR_OVERRIDES = { - btnActiveHook: '.btn.active', - btnShowHook: '.btn.show', - btnDisabledHook: '.btn.disabled', - tooltipShow: '.tooltip.show', - popoverShow: '.popover.show', - alertBtnCloseDisabledHook: '.btn-close.disabled', alertBtnClose: '.btn-close', inputFontFamily: 'input, button, select, optgroup, textarea', body: 'body', diff --git a/ve-project2/src/components/ui/accordion/AccordionExample.tsx b/ve-project2/src/components/ui/accordion/AccordionExample.tsx index 9cca1a08b..59b607e79 100644 --- a/ve-project2/src/components/ui/accordion/AccordionExample.tsx +++ b/ve-project2/src/components/ui/accordion/AccordionExample.tsx @@ -4,21 +4,20 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { h4, inlineCode } from '../../../theme-contract/contents/contract.css' +import { inlineCode } from '../../../theme-contract/contents/basic/contract.css' +import { h4 } from '../../../theme-contract/contents/heading/contract.css' import { elButton, elStrong } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' +import { collapse, collapsed, show } from '../../../theme-contract/literal/contract.css' import { bodyText } from '../../../theme-contract/theme-contract.css' import { accordion, accordionBody, accordionButton, - accordionButtonCollapsed, accordionCollapse, accordionHeader, accordionItem, } from '../../../theme-contract/ui/accordion/contract.css' -import { collapse } from '../../../theme-contract/ui/navbar/contract.css' -import { show } from '../../../theme-contract/ui/navs/contract.css' export const ve2RequiredStyleFamilies: readonly Ve2StyleFamily[] = [ 'ui/accordion', @@ -70,7 +69,7 @@ const AccordionExample: Component = () => {

diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledDarkButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledDarkButton.tsx index 51244ba1d..5083d0a6b 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledDarkButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledDarkButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineDark, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledDarkButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledInfoButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledInfoButton.tsx index 1a9f15cec..3756d38f1 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledInfoButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledInfoButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineInfo, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledInfoButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledLightButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledLightButton.tsx index 70d2da01c..63dd79e63 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledLightButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledLightButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineLight, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledLightButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledPrimaryButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledPrimaryButton.tsx index a77e60f0f..b2feb39aa 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledPrimaryButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledPrimaryButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlinePrimary, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledPrimaryButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSecondaryButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSecondaryButton.tsx index a4ac3354b..b237f0c53 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSecondaryButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSecondaryButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineSecondary, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledSecondaryButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSuccessButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSuccessButton.tsx index b8ccb0417..efcab558d 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSuccessButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledSuccessButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineSuccess, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledSuccessButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledWarningButton.tsx b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledWarningButton.tsx index f2ff2a24f..1f0f32541 100644 --- a/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledWarningButton.tsx +++ b/ve-project2/src/components/ui/buttons/outline/check-active/disabled/OutlineCheckActiveDisabledWarningButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnOutlineWarning, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const OutlineCheckActiveDisabledWarningButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/sizes/check-active/CheckActiveLargeButton.tsx b/ve-project2/src/components/ui/buttons/sizes/check-active/CheckActiveLargeButton.tsx index 245f46190..fe724af25 100644 --- a/ve-project2/src/components/ui/buttons/sizes/check-active/CheckActiveLargeButton.tsx +++ b/ve-project2/src/components/ui/buttons/sizes/check-active/CheckActiveLargeButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../theme-contract/layout/container.c import { active } from '../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnLg, btnPrimary, inputFontFamily, @@ -30,7 +29,7 @@ const CheckActiveLargeButton: Component = () => {
diff --git a/ve-project2/src/components/ui/buttons/sizes/check-active/disabled/CheckActiveDisabledSmallButton.tsx b/ve-project2/src/components/ui/buttons/sizes/check-active/disabled/CheckActiveDisabledSmallButton.tsx index 20268f706..2ecd3d941 100644 --- a/ve-project2/src/components/ui/buttons/sizes/check-active/disabled/CheckActiveDisabledSmallButton.tsx +++ b/ve-project2/src/components/ui/buttons/sizes/check-active/disabled/CheckActiveDisabledSmallButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnPrimary, btnSm, inputFontFamily, @@ -31,7 +30,7 @@ const CheckActiveDisabledSmallButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/SolidCheckActiveBaseButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/SolidCheckActiveBaseButton.tsx index 1dc62d1f6..165fb9b2c 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/SolidCheckActiveBaseButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/SolidCheckActiveBaseButton.tsx @@ -8,11 +8,7 @@ import { import { elButton } from '../../../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../../../theme-contract/layout/container.css' import { active } from '../../../../../theme-contract/literal/contract.css' -import { - btn, - btnActiveHook, - inputFontFamily, -} from '../../../../../theme-contract/ui/buttons/contract.css' +import { btn, inputFontFamily } from '../../../../../theme-contract/ui/buttons/contract.css' export const ve2RequiredStyleFamilies: readonly Ve2StyleFamily[] = [ 'ui/buttons', 'contents/basic', @@ -27,7 +23,7 @@ const SolidCheckActiveBaseButton: Component = () => {
diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDangerButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDangerButton.tsx index e7e15ba1f..3d138f610 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDangerButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDangerButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnDanger, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledDangerButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDarkButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDarkButton.tsx index 40c0e01dc..272ab7160 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDarkButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledDarkButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnDark, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledDarkButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledInfoButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledInfoButton.tsx index 40e44ebe3..a6d382330 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledInfoButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledInfoButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnInfo, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledInfoButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLightButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLightButton.tsx index 7ad0ded58..8623d376a 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLightButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLightButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnLight, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledLightButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLinkButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLinkButton.tsx index 53b71f5be..0b1c9d34c 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLinkButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledLinkButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnLink, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledLinkButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledPrimaryButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledPrimaryButton.tsx index 048022b8a..aef77cfbc 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledPrimaryButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledPrimaryButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnPrimary, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledPrimaryButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSecondaryButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSecondaryButton.tsx index 658148d03..fb36ba2e7 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSecondaryButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSecondaryButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnSecondary, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledSecondaryButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSuccessButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSuccessButton.tsx index b21420ca6..b26b2ed35 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSuccessButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledSuccessButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnSuccess, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledSuccessButton: Component = () => { diff --git a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledWarningButton.tsx b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledWarningButton.tsx index ce60c0547..ec4b9e965 100644 --- a/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledWarningButton.tsx +++ b/ve-project2/src/components/ui/buttons/solid/check-active/disabled/SolidCheckActiveDisabledWarningButton.tsx @@ -10,7 +10,6 @@ import { containerFluid } from '../../../../../../theme-contract/layout/containe import { active } from '../../../../../../theme-contract/literal/contract.css' import { btn, - btnActiveHook, btnWarning, inputFontFamily, } from '../../../../../../theme-contract/ui/buttons/contract.css' @@ -30,7 +29,7 @@ const SolidCheckActiveDisabledWarningButton: Component = () => { diff --git a/ve-project2/src/components/ui/card-tabs/CardTabsExample.tsx b/ve-project2/src/components/ui/card-tabs/CardTabsExample.tsx index 61c51d342..7c5c3262b 100644 --- a/ve-project2/src/components/ui/card-tabs/CardTabsExample.tsx +++ b/ve-project2/src/components/ui/card-tabs/CardTabsExample.tsx @@ -5,8 +5,8 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h5, paragraph } from '../../../theme-contract/contents/contract.css' +import { link, paragraph } from '../../../theme-contract/contents/basic/contract.css' +import { h5 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { active, disabled, textCenter } from '../../../theme-contract/literal/contract.css' @@ -25,8 +25,6 @@ import { navButtonReset, navItem, navLink, - navLinkActive, - navLinkDisabled, navTabs, } from '../../../theme-contract/ui/navs/contract.css' import { col, g4, row } from '../../../theme-contract/utilities/contract.css' @@ -53,7 +51,7 @@ const CardTabsExample: Component = () => {
).extendDefaultConfig({ CLASS_NAME_CAROUSEL: runtimeClasses.carousel, - CLASS_NAME_ACTIVE: `${runtimeClasses.carouselActive} ${active} pwhook-carousel-active`, + CLASS_NAME_ACTIVE: `${active} pwhook-carousel-active`, CLASS_NAME_SLIDE: runtimeClasses.carouselSlide, CLASS_NAME_END: runtimeClasses.carouselItemEnd, CLASS_NAME_START: runtimeClasses.carouselItemStart, diff --git a/ve-project2/src/components/ui/dropdowns/AlignedDropdown.tsx b/ve-project2/src/components/ui/dropdowns/AlignedDropdown.tsx index f9db17407..7e98b0f28 100644 --- a/ve-project2/src/components/ui/dropdowns/AlignedDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/AlignedDropdown.tsx @@ -1,21 +1,19 @@ import type { Component } from 'solid-js' import { useContext } from 'solid-js' import { ThemeContext } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnSecondary, inputFontFamily } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdown, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, dropdownMenuEnd, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -37,10 +35,8 @@ const AlignedDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/EndDropdown.tsx b/ve-project2/src/components/ui/dropdowns/EndDropdown.tsx index 9447f6b88..0281b68de 100644 --- a/ve-project2/src/components/ui/dropdowns/EndDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/EndDropdown.tsx @@ -5,18 +5,16 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -34,10 +32,8 @@ const EndDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/LargeDropdown.tsx b/ve-project2/src/components/ui/dropdowns/LargeDropdown.tsx index 91e7f6959..630a6bf03 100644 --- a/ve-project2/src/components/ui/dropdowns/LargeDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/LargeDropdown.tsx @@ -5,19 +5,17 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnLg, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdown, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -35,10 +33,8 @@ const LargeDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/NormalDropdown.tsx b/ve-project2/src/components/ui/dropdowns/NormalDropdown.tsx index 2b326b714..b0713ccad 100644 --- a/ve-project2/src/components/ui/dropdowns/NormalDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/NormalDropdown.tsx @@ -1,19 +1,17 @@ import type { Component } from 'solid-js' import { useContext } from 'solid-js' import { ThemeContext } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdown, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -34,10 +32,8 @@ const NormalDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SmallDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SmallDropdown.tsx index a55965d6b..894e58551 100644 --- a/ve-project2/src/components/ui/dropdowns/SmallDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SmallDropdown.tsx @@ -5,19 +5,17 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnSecondary, btnSm } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdown, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -35,10 +33,8 @@ const SmallDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitDangerDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitDangerDropdown.tsx index 3a8433b49..bff71a25b 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitDangerDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitDangerDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnDanger } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitDangerDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitDarkDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitDarkDropdown.tsx index 98afc62a6..3e2d0a69a 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitDarkDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitDarkDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnDark } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitDarkDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitInfoDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitInfoDropdown.tsx index 1342f7e92..b2cf938a9 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitInfoDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitInfoDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnInfo } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitInfoDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitLightDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitLightDropdown.tsx index 496952340..d8b55b905 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitLightDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitLightDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnLight } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitLightDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitPrimaryDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitPrimaryDropdown.tsx index d30987548..05bc59b7c 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitPrimaryDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitPrimaryDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnPrimary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitPrimaryDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitSecondaryDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitSecondaryDropdown.tsx index 4e9aa3dad..369a3fbcf 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitSecondaryDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitSecondaryDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitSecondaryDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitSuccessDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitSuccessDropdown.tsx index 78ab5068b..8ed09bc29 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitSuccessDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitSuccessDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnSuccess } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitSuccessDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/SplitWarningDropdown.tsx b/ve-project2/src/components/ui/dropdowns/SplitWarningDropdown.tsx index 1d4a6f1f1..15a5f3e0e 100644 --- a/ve-project2/src/components/ui/dropdowns/SplitWarningDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/SplitWarningDropdown.tsx @@ -11,10 +11,8 @@ import { containerFluid } from '../../../theme-contract/layout/container.css' import { btnGroup } from '../../../theme-contract/ui/button-group/contract.css' import { btn, btnWarning } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropdownToggleSplit, dropend, @@ -35,10 +33,8 @@ const SplitWarningDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/StartDropdown.tsx b/ve-project2/src/components/ui/dropdowns/StartDropdown.tsx index 4fd0d5efd..a2646cd81 100644 --- a/ve-project2/src/components/ui/dropdowns/StartDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/StartDropdown.tsx @@ -5,18 +5,16 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -34,10 +32,8 @@ const StartDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/UpDropdown.tsx b/ve-project2/src/components/ui/dropdowns/UpDropdown.tsx index 4b291793b..0bef5d145 100644 --- a/ve-project2/src/components/ui/dropdowns/UpDropdown.tsx +++ b/ve-project2/src/components/ui/dropdowns/UpDropdown.tsx @@ -5,18 +5,16 @@ import { useVe2RequiredStyleFamilies, type Ve2StyleFamily, } from '../../../context/ThemeContext' -import { link } from '../../../theme-contract/contents/basic/contract.css' -import { h6, horizontalRule } from '../../../theme-contract/contents/contract.css' +import { horizontalRule, link } from '../../../theme-contract/contents/basic/contract.css' +import { h6 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' import { btn, btnSecondary } from '../../../theme-contract/ui/buttons/contract.css' import { - btnShowHook, dropdownDivider, dropdownHeader, dropdownItem, dropdownMenu, - dropdownMenuShow, dropdownToggle, dropend, dropstart, @@ -34,10 +32,8 @@ const UpDropdown: Component = () => { const theme = useContext(ThemeContext) useVe2RequiredStyleFamilies(ve2RequiredStyleFamilies) configureVeDropdown({ - btnShowHook, dropdownItem, dropdownMenu, - dropdownMenuShow, dropend, dropstart, dropup, diff --git a/ve-project2/src/components/ui/dropdowns/ve-dropdown.ts b/ve-project2/src/components/ui/dropdowns/ve-dropdown.ts index b3b0cecc8..d15444b90 100644 --- a/ve-project2/src/components/ui/dropdowns/ve-dropdown.ts +++ b/ve-project2/src/components/ui/dropdowns/ve-dropdown.ts @@ -1,13 +1,12 @@ import * as bootstrap from 'bootstrap' import { getVarName } from '../../../logic/veGetVarName' +import { show } from '../../../theme-contract/literal/contract.css' import { varBsPosition } from '../../../theme-contract/utilities/generated/_vars.css' import type { BootstrapWithDefaults } from '../bootstrapWithDefaults' interface VeDropdownRuntimeClasses { - btnShowHook: string dropdownItem: string dropdownMenu: string - dropdownMenuShow: string dropend: string dropstart: string dropup: string @@ -19,8 +18,11 @@ export function createVeDropdown( return ( bootstrap.Dropdown as unknown as BootstrapWithDefaults ).extendDefaultConfig({ - CLASS_NAME_SHOW_TRIGGER: runtimeClasses.btnShowHook, - CLASS_NAME_SHOW_MENU: runtimeClasses.dropdownMenuShow, + // Both the trigger button and the open menu carry Bootstrap's `.show`; the + // source rules are `.btn.show` and `.dropdown-menu.show`, so one contract serves + // both and the compound selectors keep them apart. + CLASS_NAME_SHOW_TRIGGER: show, + CLASS_NAME_SHOW_MENU: show, CLASS_NAME_DROPUP: runtimeClasses.dropup, CLASS_NAME_DROPEND: runtimeClasses.dropend, CLASS_NAME_DROPSTART: runtimeClasses.dropstart, diff --git a/ve-project2/src/components/ui/list-group/DefaultListGroup.tsx b/ve-project2/src/components/ui/list-group/DefaultListGroup.tsx index fdccce1ac..6f0099802 100644 --- a/ve-project2/src/components/ui/list-group/DefaultListGroup.tsx +++ b/ve-project2/src/components/ui/list-group/DefaultListGroup.tsx @@ -7,11 +7,7 @@ import { } from '../../../context/ThemeContext' import { containerFluid } from '../../../theme-contract/layout/container.css' import { disabled } from '../../../theme-contract/literal/contract.css' -import { - listGroup, - listGroupItem, - listGroupItemDisabled, -} from '../../../theme-contract/ui/list-group/contract.css' +import { listGroup, listGroupItem } from '../../../theme-contract/ui/list-group/contract.css' export const ve2RequiredStyleFamilies: readonly Ve2StyleFamily[] = [ 'ui/list-group', @@ -25,10 +21,7 @@ const DefaultListGroup: Component = () => { return (
    -
  • +
  • A disabled item
  • A second item
  • diff --git a/ve-project2/src/components/ui/modal/CenteredScrollableModal.tsx b/ve-project2/src/components/ui/modal/CenteredScrollableModal.tsx index e59b84d7a..7d6fee749 100644 --- a/ve-project2/src/components/ui/modal/CenteredScrollableModal.tsx +++ b/ve-project2/src/components/ui/modal/CenteredScrollableModal.tsx @@ -9,7 +9,7 @@ import { paragraph } from '../../../theme-contract/contents/basic/contract.css' import { h5 } from '../../../theme-contract/contents/heading/contract.css' import { elButton } from '../../../theme-contract/global-elements/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' -import { btnClose } from '../../../theme-contract/literal/contract.css' +import { btnClose, fade, show } from '../../../theme-contract/literal/contract.css' import { bodyText } from '../../../theme-contract/theme-contract.css' import { alertBtnClose } from '../../../theme-contract/ui/alerts/contract.css' import { @@ -29,11 +29,9 @@ import { modalDialog, modalDialogCentered, modalDialogScrollable, - modalFade, modalFooter, modalHeader, modalOpenHook, - modalShowHook, modalTitle, } from '../../../theme-contract/ui/modal/contract.css' import { dFlex } from '../../../theme-contract/utilities/contract.css' @@ -58,7 +56,7 @@ const CenteredScrollableModal: Component = () => { modalBody, modalDialog, modalOpenHook: `${theme} ${modalOpenHook}`, - modalShowHook, + show, }) return ( <> @@ -75,7 +73,7 @@ const CenteredScrollableModal: Component = () => {

).extendDefaultConfig({ - CLASS_NAME_FADE: modalFade, - CLASS_NAME_SHOW: runtime.modalShowHook, + CLASS_NAME_FADE: fade, + CLASS_NAME_SHOW: show, className: runtime.modalBackdrop, // @ts-expect-error bootstrap runtime exposes this but types do not }) as typeof bootstrap.Backdrop, diff --git a/ve-project2/src/components/ui/navbar/NavbarExample.tsx b/ve-project2/src/components/ui/navbar/NavbarExample.tsx index bcdb0dad4..25e9560bf 100644 --- a/ve-project2/src/components/ui/navbar/NavbarExample.tsx +++ b/ve-project2/src/components/ui/navbar/NavbarExample.tsx @@ -14,6 +14,7 @@ import { alignTop, bgLight, bgPrimary, + collapse, dInlineBlock, disabled, navbarDark, @@ -33,7 +34,6 @@ import { dropdownToggle, } from '../../../theme-contract/ui/dropdowns/contract.css' import { - collapse, navbar, navbarBgLight, navbarBrand, @@ -44,12 +44,7 @@ import { navbarToggler, navbarTogglerIcon, } from '../../../theme-contract/ui/navbar/contract.css' -import { - navItem, - navLink, - navLinkActive, - navLinkDisabled, -} from '../../../theme-contract/ui/navs/contract.css' +import { navItem, navLink } from '../../../theme-contract/ui/navs/contract.css' import { dFlex, mb2, mbLg0, me2, meAuto, mt5 } from '../../../theme-contract/utilities/contract.css' export const ve2RequiredStyleFamilies: readonly Ve2StyleFamily[] = [ @@ -104,7 +99,7 @@ const NavbarExample: Component = () => {
  • {/* biome-ignore lint: is used for demonstration purposes */} e.preventDefault()} @@ -175,7 +170,7 @@ const NavbarExample: Component = () => {
  • {/* biome-ignore lint: is used for demonstration purposes */} e.preventDefault()} tabindex="-1" @@ -239,7 +234,7 @@ const NavbarExample: Component = () => {
  • {/* biome-ignore lint: is used for demonstration purposes */} e.preventDefault()} @@ -310,7 +305,7 @@ const NavbarExample: Component = () => {
  • {/* biome-ignore lint: is used for demonstration purposes */} e.preventDefault()} tabindex="-1" diff --git a/ve-project2/src/components/ui/navs/BasicNav.tsx b/ve-project2/src/components/ui/navs/BasicNav.tsx index 7f6f8cd98..f3552f4cd 100644 --- a/ve-project2/src/components/ui/navs/BasicNav.tsx +++ b/ve-project2/src/components/ui/navs/BasicNav.tsx @@ -7,12 +7,8 @@ import { } from '../../../context/ThemeContext' import { link } from '../../../theme-contract/contents/basic/contract.css' import { containerFluid } from '../../../theme-contract/layout/container.css' -import { - nav, - navLink, - navLinkActive, - navLinkDisabled, -} from '../../../theme-contract/ui/navs/contract.css' +import { active, disabled } from '../../../theme-contract/literal/contract.css' +import { nav, navLink } from '../../../theme-contract/ui/navs/contract.css' export const ve2RequiredStyleFamilies: readonly Ve2StyleFamily[] = [ 'ui/navs', @@ -29,7 +25,7 @@ const BasicNav: Component = () => {