@godxjp/ui 19.1.0 → 19.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -128
- package/dist/app/theme-axes.d.ts +4 -15
- package/dist/app/timezones.d.ts +1 -4
- package/dist/components/charts/area-chart.d.ts +2 -3
- package/dist/components/charts/bar-chart.d.ts +2 -3
- package/dist/components/charts/chart-frame.d.ts +7 -16
- package/dist/components/charts/chart-summary.d.ts +2 -2
- package/dist/components/charts/compact-bar-trend.d.ts +9 -24
- package/dist/components/charts/index.d.ts +1 -6
- package/dist/components/data-display/badge.d.ts +20 -14
- package/dist/components/data-display/badge.js +18 -4
- package/dist/components/data-display/card.d.ts +21 -41
- package/dist/components/data-display/carousel.d.ts +1 -2
- package/dist/components/data-display/code-block.d.ts +4 -0
- package/dist/components/data-display/code-block.js +24 -0
- package/dist/components/data-display/data-table.d.ts +15 -37
- package/dist/components/data-display/data-table.js +1 -3
- package/dist/components/data-display/descriptions.d.ts +2 -6
- package/dist/components/data-display/descriptions.js +0 -2
- package/dist/components/data-display/index.d.ts +4 -0
- package/dist/components/data-display/index.js +4 -0
- package/dist/components/data-display/list-row.d.ts +3 -22
- package/dist/components/data-display/permission-matrix.d.ts +7 -20
- package/dist/components/data-display/progress.d.ts +1 -1
- package/dist/components/data-display/prose.d.ts +4 -0
- package/dist/components/data-display/prose.js +20 -0
- package/dist/components/data-display/service-launcher-card.d.ts +3 -10
- package/dist/components/data-display/table.d.ts +20 -80
- package/dist/components/data-display/table.js +0 -3
- package/dist/components/data-display/timeline.d.ts +3 -5
- package/dist/components/data-entry/branch-scope-picker.d.ts +2 -19
- package/dist/components/data-entry/branch-scope-picker.js +0 -1
- package/dist/components/data-entry/calendar.d.ts +3 -2
- package/dist/components/data-entry/calendar.js +58 -3
- package/dist/components/data-entry/command-palette.d.ts +8 -22
- package/dist/components/data-entry/date-picker.d.ts +4 -5
- package/dist/components/data-entry/date-picker.js +6 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.js +8 -3
- package/dist/components/data-entry/form-errors.d.ts +7 -18
- package/dist/components/data-entry/form-field.js +1 -5
- package/dist/components/data-entry/form.d.ts +1 -9
- package/dist/components/data-entry/input.d.ts +16 -14
- package/dist/components/data-entry/input.js +3 -0
- package/dist/components/data-entry/month-picker.js +2 -2
- package/dist/components/data-entry/month-range-picker.d.ts +4 -6
- package/dist/components/data-entry/month-range-picker.js +2 -2
- package/dist/components/data-entry/number-input.d.ts +1 -5
- package/dist/components/data-entry/number-input.js +1 -0
- package/dist/components/data-entry/radio.js +0 -1
- package/dist/components/data-entry/search-select.d.ts +2 -4
- package/dist/components/data-entry/select.d.ts +11 -6
- package/dist/components/data-entry/select.js +4 -2
- package/dist/components/data-entry/switch.js +0 -1
- package/dist/components/data-entry/textarea.d.ts +10 -16
- package/dist/components/data-entry/time-picker.d.ts +3 -5
- package/dist/components/data-entry/upload-types.d.ts +1 -5
- package/dist/components/data-entry/use-upload-draft.d.ts +0 -4
- package/dist/components/feedback/dialog.d.ts +6 -15
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +3 -10
- package/dist/components/feedback/sheet.js +1 -1
- package/dist/components/feedback/sonner.js +2 -3
- package/dist/components/general/activity.d.ts +4 -30
- package/dist/components/general/button.js +1 -10
- package/dist/components/general/logo.d.ts +6 -28
- package/dist/components/general/reveal.d.ts +3 -9
- package/dist/components/general/typography.d.ts +1 -3
- package/dist/components/layout/account-chip.d.ts +8 -0
- package/dist/components/layout/account-chip.js +45 -0
- package/dist/components/layout/auth-divider.d.ts +2 -9
- package/dist/components/layout/auth-shell.d.ts +1 -14
- package/dist/components/layout/breadcrumb.d.ts +1 -5
- package/dist/components/layout/centered-shell.d.ts +4 -18
- package/dist/components/layout/error-surface.d.ts +11 -28
- package/dist/components/layout/error-surface.js +4 -1
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/components/layout/legal-document-shell.d.ts +5 -28
- package/dist/components/layout/master-detail.d.ts +2 -18
- package/dist/components/layout/page-container.js +0 -1
- package/dist/components/layout/responsive-grid.d.ts +6 -1
- package/dist/components/layout/responsive-grid.js +9 -2
- package/dist/components/layout/service-role-panel.d.ts +7 -19
- package/dist/components/layout/sidebar.d.ts +5 -27
- package/dist/components/layout/sidebar.js +0 -1
- package/dist/components/layout/split-pane.d.ts +0 -7
- package/dist/components/layout/topbar.d.ts +1 -14
- package/dist/components/navigation/app-setting-picker.js +1 -3
- package/dist/components/navigation/filter-bar.d.ts +1 -6
- package/dist/components/navigation/pagination-utils.d.ts +2 -12
- package/dist/components/navigation/tabs-scroll.d.ts +2 -36
- package/dist/components/navigation/tabs.js +4 -8
- package/dist/components/query/data-state.d.ts +2 -2
- package/dist/components/ui/credential-reveal.d.ts +1 -4
- package/dist/components/ui/hover-card.d.ts +1 -2
- package/dist/components/ui/separator.d.ts +2 -15
- package/dist/components/ui/toggle-group.d.ts +1 -5
- package/dist/components/ui/toggle.d.ts +6 -31
- package/dist/email/brand-mark.d.ts +6 -34
- package/dist/email/color.d.ts +7 -17
- package/dist/email/geometry.d.ts +4 -8
- package/dist/email/index.d.ts +4 -48
- package/dist/email/inline-style.d.ts +3 -9
- package/dist/email/urgency.d.ts +2 -7
- package/dist/form/form-context.d.ts +2 -5
- package/dist/form/form-root.d.ts +1 -2
- package/dist/i18n/messages/en.json +4 -0
- package/dist/i18n/messages/ja.json +4 -0
- package/dist/i18n/messages/vi.json +4 -0
- package/dist/inertia/index.d.ts +2 -10
- package/dist/lib/control-styles.d.ts +10 -21
- package/dist/lib/control-styles.js +4 -6
- package/dist/lib/datetime/format-date.d.ts +1 -4
- package/dist/lib/field-a11y.d.ts +11 -68
- package/dist/lib/field-a11y.js +0 -1
- package/dist/lib/format.d.ts +4 -8
- package/dist/lib/hooks.d.ts +6 -12
- package/dist/lib/permission-grid.d.ts +3 -18
- package/dist/lib/query-error.d.ts +3 -3
- package/dist/props/components/app.prop.d.ts +15 -38
- package/dist/props/components/charts.prop.d.ts +2 -3
- package/dist/props/components/data-display.prop.d.ts +55 -72
- package/dist/props/components/data-entry.prop.d.ts +35 -40
- package/dist/props/components/form.prop.d.ts +2 -6
- package/dist/props/components/general.prop.d.ts +11 -27
- package/dist/props/components/layout.prop.d.ts +89 -260
- package/dist/props/components/navigation.prop.d.ts +27 -42
- package/dist/props/registry.d.ts +36 -1
- package/dist/props/registry.js +45 -1
- package/dist/props/vocabulary/data.prop.d.ts +18 -49
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +10 -12
- package/dist/props/vocabulary/layout.prop.d.ts +8 -30
- package/dist/props/vocabulary/shared.prop.d.ts +3 -2
- package/dist/styles/alert-layout.css +1 -32
- package/dist/styles/badge-layout.css +19 -18
- package/dist/styles/base.css +14 -113
- package/dist/styles/card-layout.css +6 -128
- package/dist/styles/chart-layout.css +2 -17
- package/dist/styles/control.css +380 -458
- package/dist/styles/core.css +21 -0
- package/dist/styles/data-display-layout.css +175 -94
- package/dist/styles/data-entry-layout.css +0 -45
- package/dist/styles/density.css +1 -30
- package/dist/styles/dialog-layout.css +5 -69
- package/dist/styles/focus-ring.css +13 -87
- package/dist/styles/fonts.css +1 -31
- package/dist/styles/form-layout.css +7 -91
- package/dist/styles/index.css +2 -20
- package/dist/styles/layout.css +38 -456
- package/dist/styles/logo-layout.css +1 -35
- package/dist/styles/motion.css +3 -42
- package/dist/styles/navigation-layout.css +3 -50
- package/dist/styles/shell-layout.css +37 -403
- package/dist/styles/table-layout.css +16 -274
- package/dist/styles/text-layout.css +7 -21
- package/dist/styles/toggle.css +1 -23
- package/dist/theme/dxs.canonical.css +1 -55
- package/dist/theme/example.service.css +1 -26
- package/dist/theme/famgia.service.css +5 -15
- package/dist/tokens/axes.css +3 -40
- package/dist/tokens/base.css +1 -4
- package/dist/tokens/components/activity.css +6 -42
- package/dist/tokens/components/badge.css +12 -13
- package/dist/tokens/components/banner.css +4 -9
- package/dist/tokens/components/card.css +32 -88
- package/dist/tokens/components/chart.css +9 -26
- package/dist/tokens/components/control.css +37 -222
- package/dist/tokens/components/data-display.css +28 -79
- package/dist/tokens/components/data-entry.css +5 -19
- package/dist/tokens/components/descriptions.css +7 -27
- package/dist/tokens/components/email.css +38 -84
- package/dist/tokens/components/error-surface.css +2 -18
- package/dist/tokens/components/feedback.css +27 -124
- package/dist/tokens/components/form.css +13 -45
- package/dist/tokens/components/legal-document.css +16 -41
- package/dist/tokens/components/list-row.css +11 -36
- package/dist/tokens/components/logo.css +2 -57
- package/dist/tokens/components/navigation.css +16 -94
- package/dist/tokens/components/scroll-area.css +2 -18
- package/dist/tokens/components/separator.css +8 -30
- package/dist/tokens/components/sheet.css +3 -14
- package/dist/tokens/components/shell.css +53 -307
- package/dist/tokens/components/sidebar.css +5 -22
- package/dist/tokens/components/table.css +19 -140
- package/dist/tokens/components/toggle.css +3 -65
- package/dist/tokens/components/upload.css +7 -43
- package/dist/tokens/foundation.css +108 -383
- package/dist/tokens/semantic/layout.css +33 -207
- package/docs/CANONICAL-CONTRACTS.md +206 -0
- package/docs/COMPONENTS.md +157 -0
- package/docs/COMPOSITION-VS-COMPONENT.md +105 -0
- package/docs/CONSUMER-RULES.md +14 -0
- package/docs/CUSTOMER-THEMING.md +162 -0
- package/docs/DATETIME.md +69 -0
- package/docs/DEVELOPMENT.md +173 -0
- package/docs/FORMS.md +143 -0
- package/docs/FRAME-A11Y-CI.md +267 -0
- package/docs/FRAME-COVERAGE-LEDGER.md +123 -0
- package/docs/FRAME-COVERAGE-REPORT.md +140 -0
- package/docs/FRAME-COVERAGE-STANDARD.md +65 -0
- package/docs/FRAME-COVERAGE.md +29 -0
- package/docs/PROPS-REGISTRY.md +61 -0
- package/docs/PROPS-VOCABULARY.md +100 -0
- package/docs/README.md +60 -0
- package/docs/REGISTRY.md +70 -0
- package/docs/SPACING.md +56 -0
- package/docs/STANDARDS-vocabulary-tokens.md +48 -0
- package/docs/TESTING.md +98 -0
- package/docs/TOKENS.md +347 -0
- package/docs/asset-modules.d.ts +46 -0
- package/docs/assets/cover-terrain.svg +6 -0
- package/docs/assets/portrait-amber.svg +5 -0
- package/docs/assets/portrait-clay.svg +5 -0
- package/docs/assets/portrait-indigo.svg +5 -0
- package/docs/assets/portrait-iris.svg +5 -0
- package/docs/assets/portrait-moss.svg +5 -0
- package/docs/assets/portrait-plum.svg +5 -0
- package/docs/assets/portrait-slate.svg +5 -0
- package/docs/charts/compact-bar-trend.tsx +328 -0
- package/docs/charts/size-tiers.tsx +192 -0
- package/docs/data-display/accordion.tsx +99 -0
- package/docs/data-display/avatar.tsx +355 -0
- package/docs/data-display/badge.tsx +283 -0
- package/docs/data-display/card/examples/detail-panel.tsx +106 -0
- package/docs/data-display/card/index.md +3 -0
- package/docs/data-display/card/index.tsx +285 -0
- package/docs/data-display/carousel.tsx +160 -0
- package/docs/data-display/charts.tsx +198 -0
- package/docs/data-display/code-block.tsx +90 -0
- package/docs/data-display/collapsible.tsx +162 -0
- package/docs/data-display/credential-reveal.tsx +145 -0
- package/docs/data-display/data-table/examples/approval-queue.tsx +212 -0
- package/docs/data-display/data-table/examples/grid-features.tsx +158 -0
- package/docs/data-display/data-table/examples/list-page.tsx +175 -0
- package/docs/data-display/data-table/index.md +5 -0
- package/docs/data-display/data-table/index.tsx +295 -0
- package/docs/data-display/descriptions.tsx +143 -0
- package/docs/data-display/empty-state.tsx +164 -0
- package/docs/data-display/hover-card.tsx +181 -0
- package/docs/data-display/list-row.tsx +420 -0
- package/docs/data-display/permission-matrix.tsx +188 -0
- package/docs/data-display/popover-touch.tsx +27 -0
- package/docs/data-display/popover.tsx +246 -0
- package/docs/data-display/progress.tsx +84 -0
- package/docs/data-display/prose.tsx +105 -0
- package/docs/data-display/qr-code.tsx +68 -0
- package/docs/data-display/scroll-area.tsx +303 -0
- package/docs/data-display/service-launcher-card.tsx +145 -0
- package/docs/data-display/stat-card.tsx +99 -0
- package/docs/data-display/table.tsx +251 -0
- package/docs/data-display/timeline.tsx +112 -0
- package/docs/data-display/touch-actions.tsx +36 -0
- package/docs/data-display/tree-list.tsx +107 -0
- package/docs/data-entry/branch-scope-picker.tsx +123 -0
- package/docs/data-entry/calendar.tsx +180 -0
- package/docs/data-entry/cascader.tsx +378 -0
- package/docs/data-entry/checkbox.tsx +256 -0
- package/docs/data-entry/color-picker.tsx +117 -0
- package/docs/data-entry/command-palette.tsx +179 -0
- package/docs/data-entry/command.tsx +262 -0
- package/docs/data-entry/country-picker-recipe.tsx +175 -0
- package/docs/data-entry/date-picker.tsx +191 -0
- package/docs/data-entry/date-range-picker.tsx +149 -0
- package/docs/data-entry/form/examples/employee-settings.tsx +267 -0
- package/docs/data-entry/form/examples/invoice-form.tsx +387 -0
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +240 -0
- package/docs/data-entry/form-field/examples/create-form.tsx +105 -0
- package/docs/data-entry/form-field/index.md +8 -0
- package/docs/data-entry/form-field/index.tsx +330 -0
- package/docs/data-entry/form-inertia.tsx +80 -0
- package/docs/data-entry/form-root.tsx +102 -0
- package/docs/data-entry/form.tsx +724 -0
- package/docs/data-entry/input-otp.tsx +231 -0
- package/docs/data-entry/input.tsx +114 -0
- package/docs/data-entry/label.tsx +100 -0
- package/docs/data-entry/month-picker.tsx +101 -0
- package/docs/data-entry/month-range-picker.tsx +115 -0
- package/docs/data-entry/number-input.tsx +240 -0
- package/docs/data-entry/password-input.tsx +102 -0
- package/docs/data-entry/password-strength.tsx +97 -0
- package/docs/data-entry/radio-group.tsx +95 -0
- package/docs/data-entry/rating.tsx +87 -0
- package/docs/data-entry/search-input.tsx +120 -0
- package/docs/data-entry/select-async-contracts.tsx +108 -0
- package/docs/data-entry/select-matrix.tsx +204 -0
- package/docs/data-entry/select.tsx +369 -0
- package/docs/data-entry/slider.tsx +130 -0
- package/docs/data-entry/switch.tsx +67 -0
- package/docs/data-entry/tag-input.tsx +108 -0
- package/docs/data-entry/textarea.tsx +209 -0
- package/docs/data-entry/time-picker.tsx +99 -0
- package/docs/data-entry/toggle-count.tsx +293 -0
- package/docs/data-entry/toggle-group.tsx +225 -0
- package/docs/data-entry/toggle.tsx +127 -0
- package/docs/data-entry/transfer.tsx +97 -0
- package/docs/data-entry/tree-select.tsx +204 -0
- package/docs/data-entry/upload.tsx +184 -0
- package/docs/feedback/alert-dialog.tsx +270 -0
- package/docs/feedback/alert.tsx +248 -0
- package/docs/feedback/banner.tsx +183 -0
- package/docs/feedback/danger-confirm.tsx +88 -0
- package/docs/feedback/dialog-touch.tsx +24 -0
- package/docs/feedback/dialog.tsx +193 -0
- package/docs/feedback/sheet.tsx +420 -0
- package/docs/feedback/skeleton.tsx +111 -0
- package/docs/feedback/toast.tsx +135 -0
- package/docs/feedback/tooltip.tsx +171 -0
- package/docs/feedback/two-factor-setup.tsx +68 -0
- package/docs/foundation/_email-specimen.ts +274 -0
- package/docs/foundation/colors.tsx +204 -0
- package/docs/foundation/density.tsx +232 -0
- package/docs/foundation/email-tokens.tsx +323 -0
- package/docs/foundation/radius-shadow.tsx +154 -0
- package/docs/foundation/spacing.tsx +211 -0
- package/docs/foundation/typography.tsx +147 -0
- package/docs/general/activity.tsx +224 -0
- package/docs/general/button/examples/destructive-confirm.tsx +90 -0
- package/docs/general/button/examples/form-actions.tsx +85 -0
- package/docs/general/button/examples/toolbar.tsx +160 -0
- package/docs/general/button/index.md +12 -0
- package/docs/general/button/index.tsx +211 -0
- package/docs/general/logo.tsx +131 -0
- package/docs/general/reveal.tsx +41 -0
- package/docs/general/typography.tsx +152 -0
- package/docs/layout/account-chip.tsx +72 -0
- package/docs/layout/admin-collection.tsx +122 -0
- package/docs/layout/app-shell.tsx +374 -0
- package/docs/layout/aspect-ratio.tsx +135 -0
- package/docs/layout/auth-account-summary.tsx +103 -0
- package/docs/layout/auth-divider.tsx +28 -0
- package/docs/layout/auth-footer.tsx +37 -0
- package/docs/layout/auth-identity.tsx +36 -0
- package/docs/layout/auth-recovery/examples/mfa-challenge.md +41 -0
- package/docs/layout/auth-recovery/examples/mfa-challenge.tsx +254 -0
- package/docs/layout/auth-recovery/examples/mobile-390.md +25 -0
- package/docs/layout/auth-recovery/examples/mobile-390.tsx +82 -0
- package/docs/layout/auth-recovery/examples/password-recovery.md +24 -0
- package/docs/layout/auth-recovery/examples/password-recovery.tsx +197 -0
- package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.md +27 -0
- package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.tsx +156 -0
- package/docs/layout/auth-recovery/index.md +140 -0
- package/docs/layout/auth-recovery/index.tsx +96 -0
- package/docs/layout/auth-shell-context.tsx +103 -0
- package/docs/layout/auth-shell-device.tsx +112 -0
- package/docs/layout/auth-shell-registration.tsx +335 -0
- package/docs/layout/auth-shell-variants.tsx +139 -0
- package/docs/layout/auth-shell.tsx +59 -0
- package/docs/layout/auth-stack.tsx +40 -0
- package/docs/layout/centered-shell.tsx +102 -0
- package/docs/layout/error-surface/examples/application-403.tsx +84 -0
- package/docs/layout/error-surface/examples/application-404.tsx +77 -0
- package/docs/layout/error-surface/examples/system-500.tsx +47 -0
- package/docs/layout/error-surface/examples/system-503.tsx +48 -0
- package/docs/layout/error-surface/index.md +153 -0
- package/docs/layout/error-surface/index.tsx +239 -0
- package/docs/layout/flex.tsx +266 -0
- package/docs/layout/legal-document-shell/_data.ts +110 -0
- package/docs/layout/legal-document-shell/examples/desktop-1440.md +12 -0
- package/docs/layout/legal-document-shell/examples/desktop-1440.tsx +83 -0
- package/docs/layout/legal-document-shell/examples/mobile-390.md +22 -0
- package/docs/layout/legal-document-shell/examples/mobile-390.tsx +70 -0
- package/docs/layout/legal-document-shell/examples/tablet-1024.md +3 -0
- package/docs/layout/legal-document-shell/examples/tablet-1024.tsx +84 -0
- package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.md +18 -0
- package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.tsx +61 -0
- package/docs/layout/legal-document-shell/index.md +51 -0
- package/docs/layout/legal-document-shell/index.tsx +101 -0
- package/docs/layout/master-detail.tsx +387 -0
- package/docs/layout/org-switcher.tsx +131 -0
- package/docs/layout/page-container.tsx +954 -0
- package/docs/layout/resizable-panel.tsx +419 -0
- package/docs/layout/responsive-grid.tsx +243 -0
- package/docs/layout/separator.tsx +327 -0
- package/docs/layout/service-role-panel.tsx +144 -0
- package/docs/layout/sidebar.tsx +609 -0
- package/docs/layout/split-pane.tsx +468 -0
- package/docs/layout/topbar.tsx +349 -0
- package/docs/navigation/app-setting-picker.tsx +142 -0
- package/docs/navigation/breadcrumb.tsx +135 -0
- package/docs/navigation/context-menu.tsx +123 -0
- package/docs/navigation/dropdown-menu.tsx +213 -0
- package/docs/navigation/filter-bar.tsx +227 -0
- package/docs/navigation/menubar.tsx +141 -0
- package/docs/navigation/navigation-menu.tsx +158 -0
- package/docs/navigation/pagination.tsx +241 -0
- package/docs/navigation/steps.tsx +265 -0
- package/docs/navigation/tabs-rtl.tsx +28 -0
- package/docs/navigation/tabs.tsx +314 -0
- package/docs/navigation/toolbar.tsx +339 -0
- package/docs/providers/app-provider.tsx +191 -0
- package/docs/providers/format-date.tsx +230 -0
- package/docs/query/button-refetch.tsx +160 -0
- package/docs/query/data-state.tsx +192 -0
- package/docs/query/infinite-query-state.tsx +93 -0
- package/docs/query/mutation-feedback.tsx +70 -0
- package/docs/query/prefetch-link.tsx +262 -0
- package/docs/query/touch-actions.tsx +68 -0
- package/docs/showcase/acme-portal.tsx +455 -0
- package/docs/showcase/acme-website.tsx +461 -0
- package/docs/showcase/case1-warehouse-dashboard.tsx +359 -0
- package/docs/showcase/case2-employee-me.tsx +444 -0
- package/docs/showcase/case3-approval-workflow.tsx +529 -0
- package/docs/showcase/case4-login.tsx +313 -0
- package/docs/showcase/case5-shift-calendar.tsx +795 -0
- package/docs/showcase/case6-agency-handy.tsx +1113 -0
- package/docs/showcase/futurelastic-web.tsx +392 -0
- package/docs/showcase/org-switcher.tsx +313 -0
- package/docs/showcase/permission-matrix.tsx +346 -0
- package/docs/showcase/public-landing.tsx +210 -0
- package/docs/showcase/settings-account-sections.tsx +559 -0
- package/docs/showcase/settings-security-mfa.tsx +145 -0
- package/docs/showcase/table-approval-queue.tsx +199 -0
- package/docs/showcase/table-bulk-actions.tsx +460 -0
- package/docs/showcase/table-compact-kintone.tsx +438 -0
- package/docs/showcase/table-conditional-format.tsx +367 -0
- package/docs/showcase/table-crud-list.tsx +544 -0
- package/docs/showcase/table-density.tsx +347 -0
- package/docs/showcase/table-expandable-rows.tsx +480 -0
- package/docs/showcase/table-filter-chips.tsx +456 -0
- package/docs/showcase/table-footer-totals.tsx +314 -0
- package/docs/showcase/table-grouped-subtotals.tsx +391 -0
- package/docs/showcase/table-master-detail.tsx +484 -0
- package/docs/showcase/table-pagination.tsx +327 -0
- package/docs/showcase/table-states.tsx +366 -0
- package/docs/showcase/table-sticky-columns.tsx +376 -0
- package/docs/showcase/table-tree-rows.tsx +444 -0
- package/docs/showcase/table-view-tabs.tsx +488 -0
- package/package.json +9 -12
- package/scripts/_agent-setup.mjs +16 -2
- package/scripts/audit-hook.mjs +3 -13
- package/scripts/init-agent-kit.mjs +1 -14
- package/scripts/postinstall.mjs +12 -12
- package/scripts/ui-audit.mjs +93 -31
- package/scripts/visual-audit-rules.mjs +51 -7
- package/scripts/visual-audit.mjs +118 -47
package/scripts/ui-audit.mjs
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* godxjp-ui audit — the design system's own linter for consumer UI code.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* use the design system, never hand-roll. Inspired by `@google/design.md lint`
|
|
8
|
-
* — structured, agent-readable findings.
|
|
9
|
-
*
|
|
10
|
-
* Runs from the CONSUMER's repo root and scans its hand-written UI
|
|
11
|
-
* (components / pages / layouts) — never the framework or generated code.
|
|
12
|
-
*
|
|
13
|
-
* Usage (from the consuming app):
|
|
14
|
-
* node packages/godx-ui/scripts/ui-audit.mjs # human report; exit 1 on error
|
|
15
|
-
* node packages/godx-ui/scripts/ui-audit.mjs --format json
|
|
16
|
-
* node packages/godx-ui/scripts/ui-audit.mjs --quiet # errors only
|
|
17
|
-
* node packages/godx-ui/scripts/ui-audit.mjs src/ui lib/ # custom scan dirs
|
|
3
|
+
* godxjp-ui audit — the design system's own linter for consumer UI code. Enforces the
|
|
4
|
+
* UI-standardization rules that ship with @godxjp/ui (see the app's
|
|
5
|
+
* .claude/skills/frontend-design/rules/ui-standardization.md): use the design system, never
|
|
6
|
+
* hand-roll.
|
|
18
7
|
*/
|
|
19
8
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
20
9
|
import { isAbsolute, join, relative } from "node:path";
|
|
@@ -54,6 +43,37 @@ const EMOJI_FLAG = /\p{Regional_Indicator}/u;
|
|
|
54
43
|
* CSS Logical Properties L1 (W3C) · HTML Living Standard (WHATWG).
|
|
55
44
|
*/
|
|
56
45
|
const RULES = [
|
|
46
|
+
{
|
|
47
|
+
id: "no-utility-spacing",
|
|
48
|
+
scope: "consumer",
|
|
49
|
+
severity: "error",
|
|
50
|
+
// Any Tailwind spacing step on the consumer's own markup. Arbitrary values ([13px]) have their
|
|
51
|
+
// own rule; this is README rule 3, documented for a long time and never enforced: rhythm
|
|
52
|
+
// comes from Flex gap / ResponsiveGrid / PageContainer, never from gap-*/p-*/m-* utilities.
|
|
53
|
+
test: /\b(?:gap|gap-x|gap-y|p|px|py|pt|pb|ps|pe|pl|pr|m|mx|my|mt|mb|ms|me|ml|mr)-(?:\d+(?:\.\d+)?|px)\b/,
|
|
54
|
+
message:
|
|
55
|
+
"No Tailwind spacing utilities (gap-*/p-*/m-*) for layout — space siblings with <Flex gap> / <ResponsiveGrid>; sections of a page are spaced by <PageContainer> (docs/CONSUMER-RULES.md §3).",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "no-utility-layout",
|
|
59
|
+
scope: "consumer",
|
|
60
|
+
severity: "error",
|
|
61
|
+
// A hand-rolled flex/grid container. The primitives exist precisely so a row is <Flex>, a
|
|
62
|
+
// stack is <Flex direction="col">, a grid is <ResponsiveGrid> — token gaps, RTL, density.
|
|
63
|
+
test: /className=(?:"[^"]*|'[^']*|\{`[^`]*)(?<![\w-])(?:flex|inline-flex|grid|inline-grid)(?![\w-])/,
|
|
64
|
+
message:
|
|
65
|
+
'No hand-rolled flex/grid — use <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid> (docs/CONSUMER-RULES.md §3).',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "no-hand-rolled-surface",
|
|
69
|
+
scope: "consumer",
|
|
70
|
+
severity: "warn",
|
|
71
|
+
// rounded + border/bg on the consumer's own element = a fake Card / Badge / Avatar / ListRow
|
|
72
|
+
// that drifts from the real ones (a 38px account pill beside 32px controls).
|
|
73
|
+
test: /className=(?:"[^"]*|'[^']*|\{`[^`]*)\brounded(?:-(?:full|sm|md|lg|xl|2xl))?\b[^"'`]*\b(?:border|bg-)/,
|
|
74
|
+
message:
|
|
75
|
+
"Hand-rolled surface (rounded + border/bg) — use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from the tokens (docs/CONSUMER-RULES.md §4).",
|
|
76
|
+
},
|
|
57
77
|
{
|
|
58
78
|
id: "no-space-xy",
|
|
59
79
|
severity: "error",
|
|
@@ -138,7 +158,6 @@ const RULES = [
|
|
|
138
158
|
// Tailwind class; this catches one in a design-system KNOB the app sets itself
|
|
139
159
|
// (`style={{ "--card-space-inset": "13px" }}`), which is the sanctioned per-instance override
|
|
140
160
|
// route and therefore the one place an app can silently leave the scale. Framework-side the same
|
|
141
|
-
// check runs over `src/tokens/**` as `pnpm check:token-scale-bypass` (gh#332).
|
|
142
161
|
//
|
|
143
162
|
// Only axes that HAVE named steps are flagged — space, font-size, radius, icon-size. width /
|
|
144
163
|
// height / size / offset have no scale yet, so a number there is the only thing to write and is
|
|
@@ -333,26 +352,30 @@ const RULES = [
|
|
|
333
352
|
* so the executable rules and the agent-facing docs never drift.
|
|
334
353
|
*/
|
|
335
354
|
if (args.includes("--rules")) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
355
|
+
await new Promise((resolve) =>
|
|
356
|
+
process.stdout.write(
|
|
357
|
+
JSON.stringify(
|
|
358
|
+
RULES.map((r) => ({
|
|
359
|
+
id: r.id,
|
|
360
|
+
severity: r.severity,
|
|
361
|
+
standard: r.standard ?? null,
|
|
362
|
+
message: r.message,
|
|
363
|
+
})),
|
|
364
|
+
null,
|
|
365
|
+
2,
|
|
366
|
+
) + "\n",
|
|
367
|
+
resolve,
|
|
368
|
+
),
|
|
347
369
|
);
|
|
370
|
+
// Exit only once the pipe has drained (a bare process.exit() cut a 367 KB report at 64 KB),
|
|
371
|
+
// and never fall through to the scan, whose summary line would corrupt the JSON.
|
|
348
372
|
process.exit(0);
|
|
349
373
|
}
|
|
350
374
|
|
|
351
375
|
/**
|
|
352
376
|
* Blank out `//` line comments and block comments (incl. JSDoc) — replacing them with spaces and
|
|
353
377
|
* preserving newlines so line numbers stay accurate — while KEEPING string/template literals (the
|
|
354
|
-
* className values we actually want to scan).
|
|
355
|
-
* literally says "Never a raw <input>".
|
|
378
|
+
* className values we actually want to scan).
|
|
356
379
|
*/
|
|
357
380
|
function stripComments(src) {
|
|
358
381
|
let out = "";
|
|
@@ -464,6 +487,25 @@ const CARD_FLUSH =
|
|
|
464
487
|
const BARE_FIELD =
|
|
465
488
|
/<(?:label|Label)\b[^>]*>[\s\S]{0,240}?<\/(?:label|Label)>\s*<(?:Input|Select|Textarea|NumberInput|SearchInput|SearchSelect|DatePicker|DateRangePicker|TimePicker|MonthPicker|MonthRangePicker|Cascader|TreeSelect|input)\b/g;
|
|
466
489
|
|
|
490
|
+
// Two <Card> siblings written back to back at the same indentation with nothing between them.
|
|
491
|
+
// Direct children of PageContainer are spaced by the page; anywhere else they touch.
|
|
492
|
+
const SIBLING_CARDS = /^([ \t]*)<\/Card>\s*\n\1<Card\b/gm;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* `scope: "consumer"` rules ban utilities on a CONSUMER's own markup. Inside the library repo the
|
|
496
|
+
* utilities are the design system itself (that is where `flex`/`gap-*` are supposed to live), so
|
|
497
|
+
* those rules are skipped when the CWD package is @godxjp/ui — unless `--consumer` forces them.
|
|
498
|
+
*/
|
|
499
|
+
const SELF = (() => {
|
|
500
|
+
try {
|
|
501
|
+
return JSON.parse(readFileSync(join(CWD, "package.json"), "utf8")).name === "@godxjp/ui";
|
|
502
|
+
} catch {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
})();
|
|
506
|
+
const ACTIVE_RULES =
|
|
507
|
+
SELF && !args.includes("--consumer") ? RULES.filter((r) => r.scope !== "consumer") : RULES;
|
|
508
|
+
|
|
467
509
|
const findings = [];
|
|
468
510
|
for (const dir of SCAN_DIRS) {
|
|
469
511
|
for (const file of walk(isAbsolute(dir) ? dir : join(CWD, dir))) {
|
|
@@ -473,7 +515,7 @@ for (const dir of SCAN_DIRS) {
|
|
|
473
515
|
const scanContent = stripComments(content); // comments blanked; strings + line numbers kept
|
|
474
516
|
const scanLines = scanContent.split("\n");
|
|
475
517
|
scanLines.forEach((line, i) => {
|
|
476
|
-
for (const rule of
|
|
518
|
+
for (const rule of ACTIVE_RULES) {
|
|
477
519
|
if (
|
|
478
520
|
rule.test.test(line) &&
|
|
479
521
|
!isSuppressed(rule.id, origLines[i], origLines[i - 1]) &&
|
|
@@ -505,6 +547,25 @@ for (const dir of SCAN_DIRS) {
|
|
|
505
547
|
snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
|
|
506
548
|
});
|
|
507
549
|
}
|
|
550
|
+
if (
|
|
551
|
+
!(SELF && !args.includes("--consumer")) &&
|
|
552
|
+
!/<(?:Flex|ResponsiveGrid|PageContainer)\b/.test(scanContent)
|
|
553
|
+
) {
|
|
554
|
+
for (const match of scanContent.matchAll(SIBLING_CARDS)) {
|
|
555
|
+
const lineNo = scanContent.slice(0, match.index).split("\n").length + 1;
|
|
556
|
+
if (isSuppressed("sibling-cards-need-flex", origLines[lineNo - 1], origLines[lineNo - 2]))
|
|
557
|
+
continue;
|
|
558
|
+
findings.push({
|
|
559
|
+
file: rel,
|
|
560
|
+
line: lineNo,
|
|
561
|
+
rule: "sibling-cards-need-flex",
|
|
562
|
+
severity: "warn",
|
|
563
|
+
message:
|
|
564
|
+
'Adjacent <Card> siblings with no layout owner — wrap them in <Flex direction="col" gap="lg"> or <ResponsiveGrid> (inside PageContainer the page spaces them for you).',
|
|
565
|
+
snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
508
569
|
for (const match of scanContent.matchAll(BARE_FIELD)) {
|
|
509
570
|
const lineNo = scanContent.slice(0, match.index).split("\n").length;
|
|
510
571
|
if (
|
|
@@ -560,4 +621,5 @@ if (asJson) {
|
|
|
560
621
|
}
|
|
561
622
|
}
|
|
562
623
|
|
|
563
|
-
|
|
624
|
+
// See the note above --rules: exitCode, so a large JSON report drains fully.
|
|
625
|
+
process.exitCode = errors.length > 0 ? 1 : 0;
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Visual-audit decision logic — PURE functions, zero browser/DOM dependency.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* are fully unit-testable without a browser. The same functions are injected into
|
|
7
|
-
* the page for in-browser heuristics.
|
|
8
|
-
*
|
|
9
|
-
* Each rule cites the international standard it enforces — see VISUAL_RULES.
|
|
3
|
+
* `scripts/visual-audit.mjs` collects measurements from a REAL rendered page (Playwright) and
|
|
4
|
+
* feeds them here; the browser glue stays thin and these rules are fully unit-testable without a
|
|
5
|
+
* browser.
|
|
10
6
|
*/
|
|
11
7
|
|
|
12
8
|
/** Catalog (agent-facing mirror lives in mcp/src/data/visual-rules.ts; kept in sync by a guard). */
|
|
@@ -39,6 +35,34 @@ export const VISUAL_RULES = [
|
|
|
39
35
|
standard: "Unicode UTS #51 · WCAG 2.2 SC 1.1.1",
|
|
40
36
|
fix: "Remove emoji from rendered product text; use quiet i18n copy + a Lucide icon + a Badge tone for status.",
|
|
41
37
|
},
|
|
38
|
+
{
|
|
39
|
+
id: "css-layers-missing",
|
|
40
|
+
severity: "error",
|
|
41
|
+
category: "layout",
|
|
42
|
+
standard: "@godxjp/ui styles contract (styles / styles/core are the only entries)",
|
|
43
|
+
fix: "A component rendered without its layer: import `@godxjp/ui/styles` (or `styles/core` without fonts) instead of cherry-picking *-layout.css files. Naked menus and unsized Select rows are this.",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "control-height-mismatch",
|
|
47
|
+
severity: "error",
|
|
48
|
+
category: "layout",
|
|
49
|
+
standard: "@godxjp/ui control tier (--control-height) · Nielsen consistency heuristic",
|
|
50
|
+
fix: "Every control in one row (Flex row, PageContainer extra, footer) must share --control-height. Replace the odd one with the real primitive (Avatar/Button/Badge) instead of a hand-rolled pill; never restyle a control's height.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "sibling-card-gap",
|
|
54
|
+
severity: "error",
|
|
55
|
+
category: "layout",
|
|
56
|
+
standard: "@godxjp/ui spacing scale (docs/SPACING.md)",
|
|
57
|
+
fix: 'Adjacent Cards must be separated by at least one space step — wrap them in <Flex direction="col" gap> / <ResponsiveGrid>, or put them directly in PageContainer, which spaces them.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "row-content-starved",
|
|
61
|
+
severity: "warn",
|
|
62
|
+
category: "layout",
|
|
63
|
+
standard: "WCAG 2.2 SC 1.4.10 reflow (content must not be clipped to hide it)",
|
|
64
|
+
fix: 'Text in a control row is truncated to a few characters because a sibling takes the whole width (a w-full SelectTrigger). Give the Select width="auto" or move it out of the row.',
|
|
65
|
+
},
|
|
42
66
|
{
|
|
43
67
|
id: "alert-controls-misplaced",
|
|
44
68
|
severity: "warn",
|
|
@@ -114,6 +138,26 @@ export function alertControlIssues(m) {
|
|
|
114
138
|
return issues;
|
|
115
139
|
}
|
|
116
140
|
|
|
141
|
+
/** Layers whose probe rule did not resolve. @param {{layer:string, ok:boolean}[]} probes */
|
|
142
|
+
export function missingCssLayers(probes) {
|
|
143
|
+
return probes.filter((p) => !p.ok).map((p) => p.layer);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Rows whose controls disagree on height. @param {{name:string, heights:number[]}[]} rows */
|
|
147
|
+
export function controlHeightMismatches(rows) {
|
|
148
|
+
return rows.filter((r) => new Set(r.heights).size > 1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Adjacent card pairs closer than the minimum gap. @param {{gap:number}[]} pairs */
|
|
152
|
+
export function tightCardPairs(pairs, min = 8) {
|
|
153
|
+
return pairs.filter((p) => p.gap < min);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Row texts squeezed below a readable width by a sibling. @param {{text:string, visible:number, needed:number}[]} texts */
|
|
157
|
+
export function starvedRowTexts(texts) {
|
|
158
|
+
return texts.filter((t) => t.needed > 40 && t.visible < Math.min(t.needed, 60));
|
|
159
|
+
}
|
|
160
|
+
|
|
117
161
|
export function findVisualRule(id) {
|
|
118
162
|
return VISUAL_RULES.find((r) => r.id === id);
|
|
119
163
|
}
|
package/scripts/visual-audit.mjs
CHANGED
|
@@ -1,36 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* godxjp-ui VISUAL audit — the runtime counterpart to the static ui-audit.mjs.
|
|
4
|
-
*
|
|
5
|
-
* Static audit (ui-audit.mjs) reads SOURCE with regexes — zero deps, fast, runs on
|
|
6
|
-
* every save. This audit drives a REAL browser (Playwright) over the running app and
|
|
7
|
-
* runs axe-core + computed-style/layout heuristics, catching what source can't see:
|
|
8
|
-
* colour contrast, target size, OKLCH chroma of a rendered accent, emoji that survived
|
|
9
|
-
* to the DOM, and a mis-laid-out notification banner. Warnings by default (agent
|
|
10
|
-
* guidance) — pass --strict to exit non-zero. Run it BEFORE an AI/human visual review.
|
|
11
|
-
*
|
|
12
|
-
* Playwright + @axe-core/playwright are OPTIONAL peer deps — installed only by apps that
|
|
13
|
-
* run this audit, so the static audit and the library stay browser-free.
|
|
14
|
-
*
|
|
15
|
-
* TESTED peer range (see TESTED_VERSIONS + README "Runtime visual audit"):
|
|
16
|
-
* playwright >=1.55 <2 (tested 1.61.1)
|
|
17
|
-
* @axe-core/playwright >=4.10 <5 (tested 4.12.1)
|
|
18
|
-
* axe-core >=4.10 <5 (tested 4.12.1)
|
|
19
|
-
*
|
|
20
|
-
* Usage (from the consuming app, against its running dev/preview server):
|
|
21
|
-
* node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 /invoices /settings
|
|
22
|
-
* node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 --format json
|
|
23
|
-
* node node_modules/@godxjp/ui/scripts/visual-audit.mjs --strict http://localhost:5173 # CI gate
|
|
24
|
-
* PLAYWRIGHT_CHROMIUM_EXECUTABLE=/usr/bin/chromium node …/visual-audit.mjs http://… # system browser
|
|
25
|
-
*
|
|
26
|
-
* JSON contract (--format json ALWAYS emits valid JSON, even on bootstrap failure):
|
|
27
|
-
* { status: "ok" | "partial" | "error",
|
|
28
|
-
* tool: <TESTED_VERSIONS>,
|
|
29
|
-
* summary: { errors, warnings, pages, infrastructureErrors } | null,
|
|
30
|
-
* findings: [ … product findings … ],
|
|
31
|
-
* errors: [ … infrastructure errors (bootstrap / navigate / analyze) … ] }
|
|
32
|
-
* `status: "ok"` ONLY when every page was audited with zero infrastructure errors —
|
|
33
|
-
* so a tool/bootstrap failure can NEVER be misread as "zero violations".
|
|
3
|
+
* godxjp-ui VISUAL audit — the runtime counterpart to the static ui-audit.mjs. Static audit
|
|
4
|
+
* (ui-audit.mjs) reads SOURCE with regexes — zero deps, fast, runs on every save.
|
|
34
5
|
*/
|
|
35
6
|
import { pathToFileURL } from "node:url";
|
|
36
7
|
import {
|
|
@@ -40,14 +11,15 @@ import {
|
|
|
40
11
|
isUndersizedTarget,
|
|
41
12
|
hasEmoji,
|
|
42
13
|
alertControlIssues,
|
|
14
|
+
missingCssLayers,
|
|
15
|
+
controlHeightMismatches,
|
|
16
|
+
tightCardPairs,
|
|
17
|
+
starvedRowTexts,
|
|
43
18
|
} from "./visual-audit-rules.mjs";
|
|
44
19
|
|
|
45
20
|
/**
|
|
46
|
-
* Peer versions this audit is tested against. Surfaced in the JSON `tool` field, the
|
|
47
|
-
*
|
|
48
|
-
* Playwright 1.55+ is required for the `browser.newContext()` / `context.newPage()`
|
|
49
|
-
* flow that @axe-core/playwright 4.10+ expects (older `browser.newPage()` throws
|
|
50
|
-
* "Please use browser.newContext()").
|
|
21
|
+
* Peer versions this audit is tested against. Surfaced in the JSON `tool` field, the README, and
|
|
22
|
+
* the MCP `list_visual_checks` command so agents pin a compatible range.
|
|
51
23
|
*/
|
|
52
24
|
export const TESTED_VERSIONS = {
|
|
53
25
|
playwright: { range: ">=1.55 <2", tested: "1.61.1" },
|
|
@@ -68,10 +40,8 @@ export function buildFinding(ruleId, url, message) {
|
|
|
68
40
|
}
|
|
69
41
|
|
|
70
42
|
/**
|
|
71
|
-
* Assemble the machine-readable result (pure). `findings` are PRODUCT results; `errors`
|
|
72
|
-
*
|
|
73
|
-
* `status` is "ok" only when no infra error occurred, "error" when nothing could be
|
|
74
|
-
* audited, "partial" when some pages ran and some failed.
|
|
43
|
+
* Assemble the machine-readable result (pure). `findings` are PRODUCT results; `errors` are
|
|
44
|
+
* INFRASTRUCTURE failures (a page that would not load, axe that would not inject).
|
|
75
45
|
*/
|
|
76
46
|
export function buildResult({ findings = [], errors = [], pages = 0 } = {}) {
|
|
77
47
|
const productErrors = findings.filter((f) => f.severity === "error");
|
|
@@ -93,11 +63,7 @@ export function buildResult({ findings = [], errors = [], pages = 0 } = {}) {
|
|
|
93
63
|
};
|
|
94
64
|
}
|
|
95
65
|
|
|
96
|
-
/**
|
|
97
|
-
* Assemble a BOOTSTRAP failure result (pure) — missing peers, no browser, bad args.
|
|
98
|
-
* `summary` is null (never `{ warnings: 0 }`) so a consumer cannot read a bootstrap
|
|
99
|
-
* failure as a clean pass.
|
|
100
|
-
*/
|
|
66
|
+
/** Assemble a BOOTSTRAP failure result (pure) — missing peers, no browser, bad args. */
|
|
101
67
|
export function buildBootstrapError(message, hint) {
|
|
102
68
|
return {
|
|
103
69
|
status: "error",
|
|
@@ -195,7 +161,80 @@ function collectInPage() {
|
|
|
195
161
|
});
|
|
196
162
|
}
|
|
197
163
|
|
|
198
|
-
|
|
164
|
+
// Layer completeness — one probe per layer, each checks a rule only that layer sets.
|
|
165
|
+
const probe = (layer, className, prop, bad) => {
|
|
166
|
+
const el = document.createElement("div");
|
|
167
|
+
el.className = className;
|
|
168
|
+
el.style.position = "absolute";
|
|
169
|
+
el.style.visibility = "hidden";
|
|
170
|
+
document.body.appendChild(el);
|
|
171
|
+
const v = getComputedStyle(el)[prop];
|
|
172
|
+
el.remove();
|
|
173
|
+
return { layer, ok: v !== bad && v !== "" };
|
|
174
|
+
};
|
|
175
|
+
const layers = [
|
|
176
|
+
probe("control", "ui-button", "borderRadius", "0px"),
|
|
177
|
+
probe("navigation-layout", "ui-dropdown-menu-content", "borderTopWidth", "0px"),
|
|
178
|
+
probe("card-layout", "ui-card", "borderTopWidth", "0px"),
|
|
179
|
+
probe("layout", "ui-page-container", "display", "block"),
|
|
180
|
+
probe("dialog-layout", "ui-dialog-content", "position", "static"),
|
|
181
|
+
probe("form-layout", "ui-form-field", "display", "inline"),
|
|
182
|
+
];
|
|
183
|
+
// Control rows — every control in a row shares the control tier height.
|
|
184
|
+
const rows = [];
|
|
185
|
+
for (const row of document.querySelectorAll(
|
|
186
|
+
".ui-flex[data-direction=row], .ui-page-header-extra, .ui-page-footer .ui-flex, [data-slot=card-bar]",
|
|
187
|
+
)) {
|
|
188
|
+
if (!visible(row)) continue;
|
|
189
|
+
const controls = [...row.children].filter(
|
|
190
|
+
(c) =>
|
|
191
|
+
visible(c) &&
|
|
192
|
+
(c.matches(
|
|
193
|
+
"button, [role=combobox], [role=button], input, [data-slot=avatar], .ui-control, .ui-button",
|
|
194
|
+
) ||
|
|
195
|
+
c.querySelector(":scope > button, :scope > [role=combobox]")),
|
|
196
|
+
);
|
|
197
|
+
if (controls.length < 2) continue;
|
|
198
|
+
rows.push({
|
|
199
|
+
name: (row.className || row.tagName).toString().slice(0, 40),
|
|
200
|
+
heights: controls.map((c) => Math.round(c.getBoundingClientRect().height)),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// Sibling cards — the gap between consecutive cards in one parent.
|
|
204
|
+
const cardPairs = [];
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
for (const card of document.querySelectorAll(".ui-card, [data-slot=card]")) {
|
|
207
|
+
const parent = card.parentElement;
|
|
208
|
+
if (!parent || seen.has(parent)) continue;
|
|
209
|
+
seen.add(parent);
|
|
210
|
+
const cards = [...parent.children].filter(
|
|
211
|
+
(c) => c.matches(".ui-card, [data-slot=card]") && visible(c),
|
|
212
|
+
);
|
|
213
|
+
for (let i = 1; i < cards.length; i++) {
|
|
214
|
+
const a = cards[i - 1].getBoundingClientRect();
|
|
215
|
+
const b = cards[i].getBoundingClientRect();
|
|
216
|
+
const gap =
|
|
217
|
+
b.top >= a.bottom - 1 ? Math.round(b.top - a.bottom) : Math.round(b.left - a.right);
|
|
218
|
+
cardPairs.push({ gap });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Starved text — a text node in a control row squeezed far below what it needs.
|
|
222
|
+
const rowTexts = [];
|
|
223
|
+
for (const row of document.querySelectorAll(
|
|
224
|
+
".ui-flex[data-direction=row], .ui-page-footer .ui-flex",
|
|
225
|
+
)) {
|
|
226
|
+
for (const t of row.querySelectorAll("[data-slot=text], span, label")) {
|
|
227
|
+
if (!visible(t) || t.children.length) continue;
|
|
228
|
+
const cs = getComputedStyle(t);
|
|
229
|
+
if (cs.overflow !== "hidden" && cs.textOverflow !== "ellipsis") continue;
|
|
230
|
+
rowTexts.push({
|
|
231
|
+
text: (t.textContent || "").trim().slice(0, 30),
|
|
232
|
+
visible: Math.round(t.clientWidth),
|
|
233
|
+
needed: Math.round(t.scrollWidth),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { accents, targets, text, alerts, layers, rows, cardPairs, rowTexts };
|
|
199
238
|
}
|
|
200
239
|
/* c8 ignore stop */
|
|
201
240
|
|
|
@@ -223,7 +262,6 @@ async function loadDeps() {
|
|
|
223
262
|
/**
|
|
224
263
|
* Drive the browser over every target and gather findings + infra errors. Cleanup
|
|
225
264
|
* (page/context/browser) is guaranteed via finally on BOTH success and failure.
|
|
226
|
-
* A launch failure is re-tagged as a bootstrap error so it surfaces as `status:error`.
|
|
227
265
|
*/
|
|
228
266
|
/* c8 ignore start — the browser-driving glue; exercised by scripts/visual-audit-smoke.mjs in CI. */
|
|
229
267
|
async function audit(targets, { chromium, AxeBuilder }) {
|
|
@@ -296,6 +334,39 @@ async function audit(targets, { chromium, AxeBuilder }) {
|
|
|
296
334
|
buildFinding("emoji-rendered", url, `emoji rendered in product text: ${found}`),
|
|
297
335
|
);
|
|
298
336
|
}
|
|
337
|
+
for (const layer of missingCssLayers(m.layers || []))
|
|
338
|
+
findings.push(
|
|
339
|
+
buildFinding(
|
|
340
|
+
"css-layers-missing",
|
|
341
|
+
url,
|
|
342
|
+
`component layer "${layer}" is not loaded — its rules do not resolve`,
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
for (const r of controlHeightMismatches(m.rows || []))
|
|
346
|
+
findings.push(
|
|
347
|
+
buildFinding(
|
|
348
|
+
"control-height-mismatch",
|
|
349
|
+
url,
|
|
350
|
+
`row "${r.name}" mixes control heights ${[...new Set(r.heights)].join("/")}px`,
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
const tight = tightCardPairs(m.cardPairs || []);
|
|
354
|
+
if (tight.length)
|
|
355
|
+
findings.push(
|
|
356
|
+
buildFinding(
|
|
357
|
+
"sibling-card-gap",
|
|
358
|
+
url,
|
|
359
|
+
`${tight.length} adjacent Card pair(s) closer than 8px (min gap ${Math.min(...tight.map((p) => p.gap))}px)`,
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
for (const t of starvedRowTexts(m.rowTexts || []))
|
|
363
|
+
findings.push(
|
|
364
|
+
buildFinding(
|
|
365
|
+
"row-content-starved",
|
|
366
|
+
url,
|
|
367
|
+
`text "${t.text}" squeezed to ${t.visible}px of ${t.needed}px by a sibling in its row`,
|
|
368
|
+
),
|
|
369
|
+
);
|
|
299
370
|
m.alerts.forEach((al, i) => {
|
|
300
371
|
for (const issue of alertControlIssues(al))
|
|
301
372
|
findings.push(
|