@godxjp/ui 19.0.0 → 19.2.0
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/app-provider.d.ts +1 -1
- package/dist/app/app-provider.js +3 -0
- 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 +2 -12
- 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/cascader.js +3 -1
- package/dist/components/data-entry/checkbox-group.js +12 -5
- package/dist/components/data-entry/checkbox.js +30 -21
- 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 +12 -3
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.js +17 -6
- package/dist/components/data-entry/form-errors.d.ts +7 -18
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +15 -6
- 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 +13 -3
- package/dist/components/data-entry/month-picker.js +6 -4
- package/dist/components/data-entry/month-range-picker.d.ts +4 -6
- package/dist/components/data-entry/month-range-picker.js +11 -5
- 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 +19 -6
- package/dist/components/data-entry/search-select.d.ts +3 -5
- package/dist/components/data-entry/search-select.js +8 -2
- package/dist/components/data-entry/select.d.ts +15 -2
- package/dist/components/data-entry/select.js +57 -10
- package/dist/components/data-entry/switch.js +9 -2
- package/dist/components/data-entry/textarea.d.ts +10 -16
- package/dist/components/data-entry/textarea.js +8 -1
- package/dist/components/data-entry/time-picker.d.ts +3 -5
- package/dist/components/data-entry/tree-select.js +3 -1
- 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 +31 -36
- package/dist/lib/field-a11y.js +16 -2
- 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 +19 -27
- package/dist/props/components/charts.prop.d.ts +2 -3
- package/dist/props/components/data-display.prop.d.ts +48 -72
- package/dist/props/components/data-entry.prop.d.ts +41 -30
- 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 +31 -0
- package/dist/props/registry.js +33 -0
- 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 +4 -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 +4 -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 +247 -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 +156 -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 +184 -0
- package/docs/data-display/popover-touch.tsx +27 -0
- package/docs/data-display/popover.tsx +245 -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 +271 -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 +170 -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 +326 -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 +106 -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 +410 -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 +202 -0
- package/docs/foundation/density.tsx +232 -0
- package/docs/foundation/email-tokens.tsx +306 -0
- package/docs/foundation/radius-shadow.tsx +152 -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 +119 -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 +262 -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 +946 -0
- package/docs/layout/resizable-panel.tsx +419 -0
- package/docs/layout/responsive-grid.tsx +235 -0
- package/docs/layout/separator.tsx +327 -0
- package/docs/layout/service-role-panel.tsx +144 -0
- package/docs/layout/sidebar.tsx +586 -0
- package/docs/layout/split-pane.tsx +451 -0
- package/docs/layout/topbar.tsx +347 -0
- package/docs/navigation/app-setting-picker.tsx +141 -0
- package/docs/navigation/breadcrumb.tsx +128 -0
- package/docs/navigation/context-menu.tsx +117 -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 +165 -0
- package/docs/navigation/pagination.tsx +241 -0
- package/docs/navigation/steps.tsx +263 -0
- package/docs/navigation/tabs-rtl.tsx +28 -0
- package/docs/navigation/tabs.tsx +308 -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 +441 -0
- package/docs/showcase/acme-website.tsx +455 -0
- package/docs/showcase/case1-warehouse-dashboard.tsx +343 -0
- package/docs/showcase/case2-employee-me.tsx +441 -0
- package/docs/showcase/case3-approval-workflow.tsx +527 -0
- package/docs/showcase/case4-login.tsx +307 -0
- package/docs/showcase/case5-shift-calendar.tsx +795 -0
- package/docs/showcase/case6-agency-handy.tsx +1072 -0
- package/docs/showcase/futurelastic-web.tsx +390 -0
- package/docs/showcase/org-switcher.tsx +313 -0
- package/docs/showcase/permission-matrix.tsx +343 -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 +340 -0
- package/docs/showcase/table-expandable-rows.tsx +471 -0
- package/docs/showcase/table-filter-chips.tsx +461 -0
- package/docs/showcase/table-footer-totals.tsx +311 -0
- package/docs/showcase/table-grouped-subtotals.tsx +388 -0
- package/docs/showcase/table-master-detail.tsx +479 -0
- package/docs/showcase/table-pagination.tsx +320 -0
- package/docs/showcase/table-states.tsx +363 -0
- package/docs/showcase/table-sticky-columns.tsx +373 -0
- package/docs/showcase/table-tree-rows.tsx +444 -0
- package/docs/showcase/table-view-tabs.tsx +488 -0
- package/package.json +10 -11
- 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/dist/styles/layout.css
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* LAYOUT — Flex / Page shell / EmptyState.
|
|
3
|
-
* Apps: <Flex gap> · <PageContainer> — never ui-flex-* or Tailwind gap-*.
|
|
4
|
-
*/
|
|
1
|
+
|
|
5
2
|
|
|
6
3
|
@layer components {
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
* Two shapes share one class. WITHOUT a label the element IS the rule, exactly as before: the
|
|
10
|
-
* `:not([data-labelled])` guard keeps the plain form byte-identical (the `1px` and the `--border`
|
|
11
|
-
* are now `--separator-rule-{size,color}`, whose defaults are those same values). WITH a label
|
|
12
|
-
* the root becomes the three-cell grid `rule · label · rule` — `minmax(0, 1fr)` on both halves so
|
|
13
|
-
* a long ja/vi label clamps the rules instead of overflowing the container, and `auto` on the
|
|
14
|
-
* label so it is never squeezed to zero. `labelAlign` only re-measures the SHORT half, and it is
|
|
15
|
-
* a grid TRACK on the inline axis, so start/end swap under `dir="rtl"` with no extra rule.
|
|
16
|
-
*
|
|
17
|
-
* `tone` re-points the two colour knobs on the root rather than restating `background`/`color`
|
|
18
|
-
* per tone, so one declaration paints both shapes and a service override still wins. */
|
|
4
|
+
|
|
19
5
|
.ui-separator {
|
|
20
6
|
flex-shrink: 0;
|
|
21
7
|
}
|
|
@@ -59,8 +45,7 @@
|
|
|
59
45
|
font-size: var(--separator-label-font-size);
|
|
60
46
|
font-weight: var(--separator-label-font-weight);
|
|
61
47
|
line-height: var(--separator-label-line-height);
|
|
62
|
-
|
|
63
|
-
* grid past its container. */
|
|
48
|
+
|
|
64
49
|
overflow-wrap: anywhere;
|
|
65
50
|
}
|
|
66
51
|
|
|
@@ -98,11 +83,6 @@
|
|
|
98
83
|
overflow: hidden;
|
|
99
84
|
}
|
|
100
85
|
|
|
101
|
-
/* Decorative brand SPOTLIGHT backdrop — the token-driven radial halo behind a hero / auth card,
|
|
102
|
-
* replacing a hand-authored `radial-gradient(... hsl(var(--primary)/.18) ...)`. Apply to an
|
|
103
|
-
* `aria-hidden` layer (e.g. an absolutely-positioned full-bleed div behind the card). All geometry
|
|
104
|
-
* and tint come from the `--brand-glow*` tokens, so a theme retints/repositions it with no markup
|
|
105
|
-
* change. `pointer-events:none` keeps the backdrop from stealing clicks from the card above it. */
|
|
106
86
|
.ui-brand-glow {
|
|
107
87
|
background-image: var(--brand-glow);
|
|
108
88
|
pointer-events: none;
|
|
@@ -181,9 +161,7 @@
|
|
|
181
161
|
.ui-flex-gap-xs {
|
|
182
162
|
gap: var(--space-stack-xs);
|
|
183
163
|
}
|
|
184
|
-
|
|
185
|
-
* Without this, `gap="xs"` stacks (e.g. ToolbarGroup label + control) laid out
|
|
186
|
-
* as a row and squeezed CJK labels into a vertical wrap. */
|
|
164
|
+
|
|
187
165
|
.ui-stack-xs {
|
|
188
166
|
flex-direction: column;
|
|
189
167
|
gap: var(--space-stack-xs);
|
|
@@ -250,12 +228,6 @@
|
|
|
250
228
|
gap: var(--space-inline-lg);
|
|
251
229
|
}
|
|
252
230
|
|
|
253
|
-
/* ResponsiveGrid OWNS its query container. The component wraps the grid in
|
|
254
|
-
* `.ui-responsive-grid-scope` (container-type: inline-size, named `responsive-grid`) so the
|
|
255
|
-
* column breakpoints resolve against the WIDTH AVAILABLE TO THE GRID — not the viewport and not
|
|
256
|
-
* an undeclared ancestor `container-type`. This makes it correct inside a narrow card, a
|
|
257
|
-
* SplitPane aside, or a bare `<div>`; it can never silently stay one column for want of an
|
|
258
|
-
* external container context (gh#165). Named so it queries its own scope, never a closer one. */
|
|
259
231
|
.ui-responsive-grid-scope {
|
|
260
232
|
container: responsive-grid / inline-size;
|
|
261
233
|
}
|
|
@@ -266,32 +238,26 @@
|
|
|
266
238
|
grid-template-columns: minmax(0, 1fr);
|
|
267
239
|
}
|
|
268
240
|
|
|
241
|
+
.ui-responsive-grid[data-gap="xs"] {
|
|
242
|
+
gap: var(--space-stack-xs);
|
|
243
|
+
}
|
|
244
|
+
.ui-responsive-grid[data-gap="sm"] {
|
|
245
|
+
gap: var(--space-stack-sm);
|
|
246
|
+
}
|
|
247
|
+
.ui-responsive-grid[data-gap="md"] {
|
|
248
|
+
gap: var(--space-stack-md);
|
|
249
|
+
}
|
|
250
|
+
.ui-responsive-grid[data-gap="lg"] {
|
|
251
|
+
gap: var(--space-stack-lg);
|
|
252
|
+
}
|
|
253
|
+
.ui-responsive-grid[data-gap="xl"] {
|
|
254
|
+
gap: var(--space-stack-xl);
|
|
255
|
+
}
|
|
256
|
+
|
|
269
257
|
.ui-responsive-grid > * {
|
|
270
258
|
min-width: 0;
|
|
271
259
|
}
|
|
272
260
|
|
|
273
|
-
/* MasterDetail owns its tracks AND its collapse threshold — both token-driven (gh#223).
|
|
274
|
-
*
|
|
275
|
-
* WHY FLEX AND NOT `@container (min-width: …)`: a media/container-query CONDITION cannot read a
|
|
276
|
-
* `var()` — it is resolved before custom properties, so a tokenised breakpoint is impossible
|
|
277
|
-
* there (the reason the first cut hard-coded `40rem`, violating rule #45). The flex threshold
|
|
278
|
-
* below ("Holy Albatross") puts the breakpoint in a `calc()`, where a `var()` DOES resolve, so
|
|
279
|
-
* `--master-detail-collapse-below` is a genuine knob a service theme can retune once globally.
|
|
280
|
-
*
|
|
281
|
-
* How it reads: the fluid region's flex-basis is `(threshold - 100%) * 999`, where 100% is the
|
|
282
|
-
* composition's own inline size (so, like the old container query, the decision comes from the
|
|
283
|
-
* space available to the layout — never the viewport).
|
|
284
|
-
* · width < threshold → a huge positive basis → each region takes a whole flex line and
|
|
285
|
-
* shrinks to it: master stacked above detail, both full width.
|
|
286
|
-
* · width ≥ threshold → the calc goes negative and is clamped to 0 → one line: the fluid
|
|
287
|
-
* region absorbs the free space while the rail keeps its token width (`max()` keeps the
|
|
288
|
-
* rail's basis at the preset because a negative operand can never win a max).
|
|
289
|
-
*
|
|
290
|
-
* `data-rail` picks WHICH region is the fixed rail. Default `detail` = the canonical
|
|
291
|
-
* "fluid list + fixed detail rail" (1fr / 320px) composition; `master` puts the fixed rail on
|
|
292
|
-
* the leading edge for a category/navigator list. DOM order is always master → detail, so the
|
|
293
|
-
* stacked order is list-then-detail either way, and the flex row follows the writing direction
|
|
294
|
-
* (RTL-safe without a single physical property). */
|
|
295
261
|
.ui-master-detail {
|
|
296
262
|
display: flex;
|
|
297
263
|
flex-wrap: wrap;
|
|
@@ -303,7 +269,6 @@
|
|
|
303
269
|
--master-detail-rail-size: var(--master-detail-rail-compact);
|
|
304
270
|
}
|
|
305
271
|
|
|
306
|
-
/* Per-instance override of the token threshold (theme sets it globally, prop overrides here). */
|
|
307
272
|
.ui-master-detail[data-collapse-below="false"] {
|
|
308
273
|
--master-detail-collapse-below: 0px;
|
|
309
274
|
}
|
|
@@ -325,31 +290,17 @@
|
|
|
325
290
|
min-inline-size: 0;
|
|
326
291
|
}
|
|
327
292
|
|
|
328
|
-
/* The fluid region. */
|
|
329
293
|
.ui-master-detail[data-rail="detail"] > .ui-master-detail-master,
|
|
330
294
|
.ui-master-detail[data-rail="master"] > .ui-master-detail-detail {
|
|
331
295
|
flex: 1 1 calc((var(--master-detail-collapse-below) - 100%) * 999);
|
|
332
296
|
}
|
|
333
297
|
|
|
334
|
-
/* The fixed rail. */
|
|
335
298
|
.ui-master-detail[data-rail="detail"] > .ui-master-detail-detail,
|
|
336
299
|
.ui-master-detail[data-rail="master"] > .ui-master-detail-master {
|
|
337
300
|
flex: 0 1
|
|
338
301
|
max(var(--master-detail-rail-size), calc((var(--master-detail-collapse-below) - 100%) * 999));
|
|
339
302
|
}
|
|
340
303
|
|
|
341
|
-
/* Bounded master viewport (gh#231) — OPT-IN via `masterViewport`. `auto` (the default) emits no
|
|
342
|
-
* bound at all, so an existing composition renders byte-identically; the two presets cap the
|
|
343
|
-
* collection's BLOCK size with a token and scroll it INSIDE the region, which is what keeps a
|
|
344
|
-
* long real collection from pushing the detail thousands of pixels below the fold once the
|
|
345
|
-
* layout stacks. Block axis only — the inline axis stays owned by the flex threshold above, so
|
|
346
|
-
* the bound is writing-direction agnostic (RTL-safe).
|
|
347
|
-
*
|
|
348
|
-
* A11y: the region carries tabIndex={0} in this mode (see master-detail.tsx) so the scroll
|
|
349
|
-
* container is reachable and scrollable by keyboard alone, and the inset below reserves room
|
|
350
|
-
* for a focused row's focus ring — an overflow container clips BOTH axes, so a ring drawn at
|
|
351
|
-
* the region's edge would otherwise be cut. Focus is never trapped: the region is a plain tab
|
|
352
|
-
* stop that Tab/Shift+Tab leave normally. */
|
|
353
304
|
.ui-master-detail[data-master-viewport="compact"] > .ui-master-detail-master,
|
|
354
305
|
.ui-master-detail[data-master-viewport="standard"] > .ui-master-detail-master {
|
|
355
306
|
overflow-y: auto;
|
|
@@ -366,10 +317,6 @@
|
|
|
366
317
|
max-block-size: var(--master-detail-master-viewport-standard);
|
|
367
318
|
}
|
|
368
319
|
|
|
369
|
-
/* ── ServiceRolePanel (gh#319) ─────────────────────────────────────────────────
|
|
370
|
-
* MasterDetail above owns every bit of the two-track geometry; what follows is only the role
|
|
371
|
-
* LIST that sits in the master rail. `.ui-service-role-panel` was a bare hook (class emitted,
|
|
372
|
-
* no rule anywhere), so all of this used to be Tailwind literals on the component. */
|
|
373
320
|
.ui-service-role-panel-list {
|
|
374
321
|
display: flex;
|
|
375
322
|
flex-direction: column;
|
|
@@ -377,27 +324,16 @@
|
|
|
377
324
|
list-style: none;
|
|
378
325
|
}
|
|
379
326
|
|
|
380
|
-
/* One role: the select button plus the optional delete button, as SIBLINGS (never nested
|
|
381
|
-
* interactive controls). Deliberately NOT `.ui-inline-xs` — that helper wraps, and the icon
|
|
382
|
-
* button must stay on the row beside the flex-1 select button at every width. */
|
|
383
327
|
.ui-service-role-panel-row {
|
|
384
328
|
display: flex;
|
|
385
329
|
align-items: center;
|
|
386
330
|
gap: var(--service-role-panel-row-gap);
|
|
387
331
|
}
|
|
388
332
|
|
|
389
|
-
/* ORDERING TRAP (gh#319): this needs the `.ui-service-role-panel` ancestor. The role button is
|
|
390
|
-
* a real `<Button>`, and `.ui-button--md { padding-block: var(--button-space-block) }` lives in
|
|
391
|
-
* control.css, which styles/index.css imports AFTER layout.css — same `@layer components`, so
|
|
392
|
-
* at equal (0,1,0) specificity control.css would win and the row would snap back to the single
|
|
393
|
-
* control height. (0,2,0) settles it. `h-auto` and `flex-1` stay UTILITIES on the component for
|
|
394
|
-
* the same reason inverted: they must beat the same rule from a layer that always outranks it. */
|
|
395
333
|
.ui-service-role-panel .ui-service-role-panel-item {
|
|
396
334
|
padding-block: var(--service-role-panel-item-padding-block);
|
|
397
335
|
}
|
|
398
336
|
|
|
399
|
-
/* The button's label column. `min-inline-size: 0` is what lets the name and the meta line
|
|
400
|
-
* truncate instead of forcing the rail wider (the flexbox min-content default). */
|
|
401
337
|
.ui-service-role-panel-item-body {
|
|
402
338
|
display: flex;
|
|
403
339
|
flex-direction: column;
|
|
@@ -431,11 +367,6 @@
|
|
|
431
367
|
}
|
|
432
368
|
}
|
|
433
369
|
|
|
434
|
-
/* SplitPane OWNS its query container too — `.ui-split-pane-scope`
|
|
435
|
-
* (container-type: inline-size, named `split-pane`). The two-column layout is chosen from the
|
|
436
|
-
* pane's OWN available width, so a narrow embedded pane on a large viewport correctly stays a
|
|
437
|
-
* single column, and a wide pane on a small viewport can split (gh#165 — replaces the old
|
|
438
|
-
* `@media (min-width: 1080px)` viewport query that ignored the container). */
|
|
439
370
|
.ui-split-pane-scope {
|
|
440
371
|
container: split-pane / inline-size;
|
|
441
372
|
}
|
|
@@ -446,12 +377,6 @@
|
|
|
446
377
|
grid-template-columns: minmax(0, 1fr);
|
|
447
378
|
}
|
|
448
379
|
|
|
449
|
-
/* CLOSED rail — `aside={null}`. The `<aside>` element is gone, so the pane is one full-width
|
|
450
|
-
* column and there is nothing left for the gap to separate. The wrappers stay mounted (that is
|
|
451
|
-
* what keeps `children` from remounting), so this attribute is the ONLY thing that changes.
|
|
452
|
-
* The second attribute is deliberate: it lifts the rule above the `[data-aside-width]` presets
|
|
453
|
-
* in the container queries below, which sit at equal specificity, so a closed pane stays one
|
|
454
|
-
* column no matter where those blocks end up in source order. */
|
|
455
380
|
.ui-split-pane[data-aside="closed"][data-aside-width] {
|
|
456
381
|
grid-template-columns: minmax(0, 1fr);
|
|
457
382
|
gap: 0;
|
|
@@ -480,15 +405,11 @@
|
|
|
480
405
|
color: hsl(var(--muted-foreground));
|
|
481
406
|
}
|
|
482
407
|
|
|
483
|
-
/* Vertical orientation: the separator is a horizontal strip, so swap the
|
|
484
|
-
geometry — give it height instead of width. v4 sets aria-orientation. */
|
|
485
408
|
.ui-resizable-handle[aria-orientation="vertical"] {
|
|
486
409
|
width: auto;
|
|
487
410
|
height: var(--control-border-width);
|
|
488
411
|
}
|
|
489
412
|
|
|
490
|
-
/* v4 react-resizable-panels marks the active (dragging) and disabled state
|
|
491
|
-
via the data-separator attribute value, not data-state. */
|
|
492
413
|
.ui-resizable-handle:hover,
|
|
493
414
|
.ui-resizable-handle[data-separator="active"] {
|
|
494
415
|
background: hsl(var(--accent));
|
|
@@ -517,11 +438,6 @@
|
|
|
517
438
|
}
|
|
518
439
|
}
|
|
519
440
|
|
|
520
|
-
/* `lg` splits later than the other two ON PURPOSE. A rail is only useful when
|
|
521
|
-
* the column it sits beside still reads as the main one, and at the 48rem
|
|
522
|
-
* threshold a 30rem rail would leave 16.5rem of main — narrower than the rail
|
|
523
|
-
* itself, which inverts the whole point. 64rem leaves the main column 33.5rem,
|
|
524
|
-
* about twice the rail, so the page still reads main-plus-rail. */
|
|
525
441
|
@container split-pane (min-width: 64rem) {
|
|
526
442
|
.ui-split-pane[data-aside-width="lg"] {
|
|
527
443
|
grid-template-columns: minmax(0, 1fr) 30rem;
|
|
@@ -533,18 +449,10 @@
|
|
|
533
449
|
min-width: 0;
|
|
534
450
|
}
|
|
535
451
|
|
|
536
|
-
/* `--page-band-gap` is INTERNAL plumbing, not a theming knob (the same shape as
|
|
537
|
-
* --master-detail-rail-size above): the ONE resolved number that separates the page's bands,
|
|
538
|
-
* so the container's `gap` and the toolbar band's negation of it can never disagree. It has to
|
|
539
|
-
* be a named variable because the number is not always --space-section-active — `ghost` swaps
|
|
540
|
-
* it for --space-stack-md, while the 720px step and the admin-collection preset swap the
|
|
541
|
-
* section token underneath it. A toolbar rule that hard-coded
|
|
542
|
-
* `calc(-1 * var(--space-section-active))` would look correct only because those two tokens
|
|
543
|
-
* both resolve to 16px today: retune --space-section in a service theme and a ghost page's
|
|
544
|
-
* band would overlap or float by the difference, silently. Services still retune the SPACING
|
|
545
|
-
* through --space-section / --space-section-compact, exactly as before. */
|
|
546
452
|
.ui-page-container {
|
|
547
453
|
--page-band-gap: var(--space-section-active);
|
|
454
|
+
--page-body-gap: var(--space-section-active);
|
|
455
|
+
--page-body-gap: var(--space-section-active);
|
|
548
456
|
|
|
549
457
|
display: flex;
|
|
550
458
|
flex-direction: column;
|
|
@@ -553,85 +461,28 @@
|
|
|
553
461
|
gap: var(--page-band-gap);
|
|
554
462
|
}
|
|
555
463
|
|
|
556
|
-
/* `headerScale="chrome"` — the top band is CHROME, so it sits ON the frame's edge, not in a
|
|
557
|
-
* document's top margin. The shorthand above opens every page with --space-page-active-y (24px,
|
|
558
|
-
* 16px below the 720px step), which is right for a document: a title needs air above it. Chrome
|
|
559
|
-
* does not. A chat channel head, a mail thread's subject bar, an IDE tab strip are the FRAME —
|
|
560
|
-
* measured against the design of a consumer chat screen at 1512x805, the design's channel head
|
|
561
|
-
* occupies y 0..47 while the app's identical-height head started at y 24, pushing the whole top
|
|
562
|
-
* chrome down and taking those 24px straight off the scroll viewport (design 617px of transcript,
|
|
563
|
-
* app 587px). The band heights already matched; the page was simply floating.
|
|
564
|
-
*
|
|
565
|
-
* This is the SAME question `headerScale` already answers, not a second one: "is the first row a
|
|
566
|
-
* document title or the surface's own furniture". The type step (see .ui-page-title below) and
|
|
567
|
-
* the flush edge are two consequences of that one fact, which is why they ride the same
|
|
568
|
-
* attribute rather than a second prop that every real call site would have to set in lockstep.
|
|
569
|
-
*
|
|
570
|
-
* BLOCK-START ONLY, deliberately. `headerScale` names the HEADER, so it may only speak for the
|
|
571
|
-
* top edge; the bottom belongs to `stickyFooter`, which already zeroes `padding-block-end` for
|
|
572
|
-
* its own (documented) reason. A longhand, so that contract is untouched and the shorthand above
|
|
573
|
-
* still owns the default page.
|
|
574
|
-
*
|
|
575
|
-
* Chrome is a token, default quiet (rule #44): the inset is `--page-pad-block-start-chrome`,
|
|
576
|
-
* 0 by default (flush IS the quiet state for chrome), so a service whose design grid wants its
|
|
577
|
-
* chrome to breathe retunes it once instead of forking the page shell. The specificity (0,2,0)
|
|
578
|
-
* out-ranks both the base rule and the 720px block — which re-declares the TOKEN, never this
|
|
579
|
-
* padding — so the mechanism holds identically on both sides of that step. A page that never
|
|
580
|
-
* passes `headerScale` emits no attribute and is geometrically byte-identical. */
|
|
581
464
|
.ui-page-container[data-header-scale="chrome"] {
|
|
582
465
|
padding-block-start: var(--page-pad-block-start-chrome);
|
|
583
466
|
}
|
|
584
467
|
|
|
585
|
-
/* `headerScale="chrome"`, the BAND-HEIGHT consequence of that same fact (gh#331). A document
|
|
586
|
-
* header is content-height — correctly, a title is as tall as the title is. Chrome is furniture,
|
|
587
|
-
* and furniture has a band that things centre INTO; without one, the band's vertical centre is a
|
|
588
|
-
* function of the copy inside it (measured here: 42.02px with an `extra` control, 40.38px
|
|
589
|
-
* without), so nothing in the page can ever be aligned to it and no service can aim at a number.
|
|
590
|
-
*
|
|
591
|
-
* A MIN, not a height: an `lg` control or a wrapped JA/VI channel name must still fit, and
|
|
592
|
-
* `min-block-size` floors the band without ever clipping it. `justify-content: center` is the
|
|
593
|
-
* half that makes the floor useful — a taller band with top-packed content is a band with dead
|
|
594
|
-
* air under it, not a centred one — and it is inert while the knob is `auto`, because a column
|
|
595
|
-
* whose min IS its content height has nothing to distribute. Both declarations are therefore
|
|
596
|
-
* byte-identical for every page shipped before this token existed, chrome pages included.
|
|
597
|
-
*
|
|
598
|
-
* Scoped to the container attribute (0,2,0), like every other half of the chrome contract, so a
|
|
599
|
-
* document page never matches and the 720px block (which re-declares tokens, never these
|
|
600
|
-
* properties) cannot out-rank it. The band-height axis has ONE owner and it is
|
|
601
|
-
* `--app-shell-bar-height`: see tokens/semantic/layout.css for why the DEFAULT still is not it. */
|
|
602
468
|
.ui-page-container[data-header-scale="chrome"] .ui-page-header {
|
|
603
469
|
min-block-size: var(--page-header-min-block-size-chrome);
|
|
604
470
|
justify-content: center;
|
|
605
471
|
}
|
|
606
472
|
|
|
607
|
-
/* Horizontal page insets live on the header/body/footer — NOT the container —
|
|
608
|
-
* so the header divider and footer border span the full page width
|
|
609
|
-
* (full-bleed) while their content stays aligned with the body. */
|
|
610
473
|
.ui-page-header,
|
|
611
474
|
.ui-page-toolbar,
|
|
612
475
|
.ui-page-body,
|
|
613
476
|
.ui-page-footer {
|
|
614
477
|
padding-inline-start: var(--space-page-active-x);
|
|
615
478
|
padding-inline-end: var(--space-page-active-x);
|
|
479
|
+
padding-block-end: var(--page-footer-pad-block-end);
|
|
616
480
|
}
|
|
617
481
|
|
|
618
482
|
.ui-page-container--narrow .ui-page-body {
|
|
619
483
|
max-width: 42rem;
|
|
620
484
|
}
|
|
621
485
|
|
|
622
|
-
/* `measure` (gh#245 / gh#247) — a bounded page measure axis ORTHOGONAL to `variant`, so the
|
|
623
|
-
* quiet `ghost` chrome and a narrow/medium measure finally compose (they used to be the same
|
|
624
|
-
* variant axis, which is exactly why a notification feed could not have both). Unlike
|
|
625
|
-
* `variant="narrow"` — which caps only the body and leaves the header `extra` action stranded
|
|
626
|
-
* at the page edge — this caps the HEADER and the BODY to ONE shared measure, so the action
|
|
627
|
-
* ends on the same edge as the body surface. Both elements carry the identical page gutters,
|
|
628
|
-
* so their content edges line up exactly at both ends.
|
|
629
|
-
*
|
|
630
|
-
* `measure="default"` is the inert default: it emits `data-measure="default"`, which matches NO
|
|
631
|
-
* rule here, so every existing page renders byte-identically (the gh#231 `data-layout="stack"`
|
|
632
|
-
* precedent). The cap is a max, never a width — below it the page stays fluid, so 390px keeps
|
|
633
|
-
* the full compact-gutter surface. The footer is deliberately NOT capped: its border/background
|
|
634
|
-
* is page-level chrome that must span the page when `stickyFooter` pins it. */
|
|
635
486
|
.ui-page-container[data-measure="narrow"] .ui-page-header,
|
|
636
487
|
.ui-page-container[data-measure="narrow"] .ui-page-toolbar,
|
|
637
488
|
.ui-page-container[data-measure="narrow"] .ui-page-body {
|
|
@@ -644,9 +495,6 @@
|
|
|
644
495
|
max-inline-size: var(--page-measure-medium);
|
|
645
496
|
}
|
|
646
497
|
|
|
647
|
-
/* flush = full-bleed body AND toolbar band (DataTable, a full-width filter strip);
|
|
648
|
-
* header/footer keep their inset. `PageContainer.Inset` re-applies the page gutter to any
|
|
649
|
-
* padded strip inside either one, so a toolbar's own content can still align with the title. */
|
|
650
498
|
.ui-page-container--flush .ui-page-toolbar,
|
|
651
499
|
.ui-page-container--flush .ui-page-body {
|
|
652
500
|
padding-inline-start: 0;
|
|
@@ -659,38 +507,11 @@
|
|
|
659
507
|
padding-inline-end: var(--space-page-active-x);
|
|
660
508
|
}
|
|
661
509
|
|
|
662
|
-
/* `ghost` is the quiet chrome WEIGHT: no header rule, no header bottom pad. The two halves are
|
|
663
|
-
* NOT the same kind of decision, and the border half used to get it wrong.
|
|
664
|
-
*
|
|
665
|
-
* Hard-setting `none` here threw away an EXPLICIT `--page-header-divider` as well as the
|
|
666
|
-
* nothing it was meant to block — so a service could opt the page chrome into a rule and a
|
|
667
|
-
* ghost page would still refuse to draw it. Measured against the design of a consumer chat
|
|
668
|
-
* screen: the design carries TWO horizontal rules in the top chrome (channel head at y=47, work
|
|
669
|
-
* band at y=88) and the app rendered ONE (y=133) — the missing y=47 is this line eating the
|
|
670
|
-
* divider the service had already turned on. It is the identical bug that was fixed on
|
|
671
|
-
* `.ui-page-toolbar` below, and it takes the identical cure: re-declare the property from the
|
|
672
|
-
* SAME knob with a `none` fallback. Nothing inherits in, the opt-in passes through, and the
|
|
673
|
-
* default (`--page-header-divider: none`) is byte-identical to the hard-set version.
|
|
674
|
-
*
|
|
675
|
-
* `padding-bottom: 0` is untouched — THAT is the real quiet half of ghost, it answers "how loud
|
|
676
|
-
* is the chrome", not "did the service ask for a rule", and no token mediates it. */
|
|
677
510
|
.ui-page-container--ghost .ui-page-header {
|
|
678
511
|
border-bottom: var(--page-header-divider, none);
|
|
679
512
|
padding-bottom: 0;
|
|
680
513
|
}
|
|
681
514
|
|
|
682
|
-
/* The band's half of the same contract, and it must stay in step with the header rule above:
|
|
683
|
-
* neither one lets a divider INHERIT in, and both let an explicit opt-in through — otherwise a
|
|
684
|
-
* ghost page would draw a rule under the band that the header itself refuses to draw, or vice
|
|
685
|
-
* versa.
|
|
686
|
-
*
|
|
687
|
-
* Inherited, and only inherited. An earlier version hard-set `none` here, which also threw away
|
|
688
|
-
* an EXPLICIT `--page-toolbar-divider` — so a service could paint the band (ghost deliberately
|
|
689
|
-
* leaves the ground alone) but could never rule it, and the two knobs disagreed about who ghost
|
|
690
|
-
* outranks. A chat shell is the case that exposed it: a quiet header, and a workflow band that
|
|
691
|
-
* has to read as a separate surface from the transcript below it. Re-declaring the property with
|
|
692
|
-
* a `none` fallback keeps the original guarantee (nothing inherits in) while letting the opt-in
|
|
693
|
-
* through, which is what the paragraph above always meant. */
|
|
694
515
|
.ui-page-container--ghost .ui-page-toolbar {
|
|
695
516
|
border-block-end: var(--page-toolbar-divider, none);
|
|
696
517
|
}
|
|
@@ -702,15 +523,6 @@
|
|
|
702
523
|
.ui-page-container--sticky-footer {
|
|
703
524
|
min-height: 100%;
|
|
704
525
|
|
|
705
|
-
/* The container's own block-end padding must NOT stay under a sticky
|
|
706
|
-
* footer. A sticky box cannot travel into its containing block's padding
|
|
707
|
-
* area, so `bottom: 0` resolves that much ABOVE the true bottom edge —
|
|
708
|
-
* the footer floats short of the fold and page content scrolls through
|
|
709
|
-
* the gap behind it. Measured in Chromium at the 24px page padding: the
|
|
710
|
-
* footer pinned at 837px inside an 861px scroller.
|
|
711
|
-
*
|
|
712
|
-
* The space itself is still wanted, so it moves onto the body, which is
|
|
713
|
-
* what needs breathing room above the footer anyway. */
|
|
714
526
|
padding-block-end: 0;
|
|
715
527
|
}
|
|
716
528
|
|
|
@@ -727,10 +539,6 @@
|
|
|
727
539
|
background-color: hsl(var(--background));
|
|
728
540
|
}
|
|
729
541
|
|
|
730
|
-
/* footerReveal="onScroll": the sticky footer stays mounted (reserving its
|
|
731
|
-
* flow space, so toggling never reflows the body → no scroll jitter) but is
|
|
732
|
-
* slid below the fold until the header leaves the viewport, then slides up.
|
|
733
|
-
* data-revealed is toggled by an IntersectionObserver on the header. */
|
|
734
542
|
.ui-page-container--reveal-footer .ui-page-footer {
|
|
735
543
|
transform: translateY(100%);
|
|
736
544
|
opacity: 0;
|
|
@@ -758,43 +566,10 @@
|
|
|
758
566
|
gap: var(--space-stack-sm);
|
|
759
567
|
padding-bottom: var(--page-header-pad-bottom);
|
|
760
568
|
border-bottom: var(--page-header-divider);
|
|
761
|
-
|
|
762
|
-
* gradient across the page header (full-bleed) by configuring the token alone. */
|
|
569
|
+
|
|
763
570
|
background-image: var(--gradient-hero);
|
|
764
571
|
}
|
|
765
572
|
|
|
766
|
-
/* `toolbar` — the FIXED chrome band between the page header and the body: a filter strip, a
|
|
767
|
-
* status bar, a channel workflow rail. There was no slot for it before, so a page either put
|
|
768
|
-
* the strip inside the body (where `fill` scrolls it away) or hand-laid `position: sticky` at
|
|
769
|
-
* the call site — the exact "never hand-lay page chrome" the system forbids.
|
|
770
|
-
*
|
|
771
|
-
* `flex: none` is the whole contract: under `fill` the BODY is the scroll viewport (see
|
|
772
|
-
* .ui-page-container--fill below), so the band is a plain flex item OUTSIDE the scroller and
|
|
773
|
-
* cannot be squeezed by it. Sticky could not give that — sticky keeps content flowing
|
|
774
|
-
* BENEATH the pinned box, which is the half-sliced row a hand-rolled strip always shows.
|
|
775
|
-
*
|
|
776
|
-
* Chrome is a token, default quiet (rule #44): the band draws NO rule of its own, and its
|
|
777
|
-
* bottom rule falls back — at the CALL SITE, never bound at :root — to whatever the page
|
|
778
|
-
* header's divider is, so a service opting into `--page-header-divider` gets a consistent
|
|
779
|
-
* page chrome in ONE declaration and can still silence just this band with
|
|
780
|
-
* `--page-toolbar-divider: none`.
|
|
781
|
-
*
|
|
782
|
-
* The GROUND is a knob too, defaulting to `transparent` so the band looks exactly as it did
|
|
783
|
-
* before the token existed. Without it the only way to lift the band off the page ground was a
|
|
784
|
-
* `bg-card` utility at the call site — hand-laid page chrome, which is what this whole slot
|
|
785
|
-
* exists to abolish, and which cannot be re-themed per tenant. It is the `background`
|
|
786
|
-
* shorthand, not `background-color`, so a service may equally hand it a gradient.
|
|
787
|
-
*
|
|
788
|
-
* The NEGATIVE block margins are what make it read as chrome rather than as a third section.
|
|
789
|
-
* The container is a flex column with a `--page-band-gap` between every band, which is right
|
|
790
|
-
* for a document page (header / body / footer are three separate blocks) and wrong here: a
|
|
791
|
-
* band that carries a ground and a bottom rule and then FLOATS between two 16px voids divides
|
|
792
|
-
* nothing — measured on a 1512×805 chat page, 24/69 header · 85/134 band · 150/685 body, i.e.
|
|
793
|
-
* 32px of dead air wrapped around the one element whose job is to be attached. Chrome sits
|
|
794
|
-
* between the header and the scrolling body; chrome is flush. Cancelling the gap from the band
|
|
795
|
-
* itself keeps that in ONE declaration, leaves the container's rhythm intact for every page
|
|
796
|
-
* that passes no `toolbar`, and keeps `--page-toolbar-pad-block` as what it always was: the
|
|
797
|
-
* band's own INSIDE, the only place it is allowed to breathe. */
|
|
798
573
|
.ui-page-toolbar {
|
|
799
574
|
flex: none;
|
|
800
575
|
margin-block: calc(-1 * var(--page-band-gap));
|
|
@@ -803,9 +578,6 @@
|
|
|
803
578
|
border-block-end: var(--page-toolbar-divider, var(--page-header-divider));
|
|
804
579
|
}
|
|
805
580
|
|
|
806
|
-
/* The band is never the FIRST child — the header always renders — but it CAN be the last one
|
|
807
|
-
* (a page given a `toolbar` and no `children`). There is no gap under a last flex item to
|
|
808
|
-
* cancel, so the negation would eat the container's own bottom padding instead. */
|
|
809
581
|
.ui-page-toolbar:last-child {
|
|
810
582
|
margin-block-end: 0;
|
|
811
583
|
}
|
|
@@ -817,12 +589,6 @@
|
|
|
817
589
|
gap: var(--space-stack-sm);
|
|
818
590
|
}
|
|
819
591
|
|
|
820
|
-
/* The title/subtitle column is the SHRINKABLE side of the header row (gh#300): a flex item's
|
|
821
|
-
* automatic minimum size is its content, so without this the heading refuses to shrink and the
|
|
822
|
-
* whole overflow is charged to `extra`. The component used to carry it as a bare `min-w-0`
|
|
823
|
-
* utility, which meant the >=640px arrangement depended on a Tailwind class while the compact
|
|
824
|
-
* arrangement below declared it in CSS — the same property owned in two places (gh#319).
|
|
825
|
-
* Unconditional, so both arrangements read one rule. */
|
|
826
592
|
.ui-page-header-heading {
|
|
827
593
|
min-inline-size: 0;
|
|
828
594
|
}
|
|
@@ -834,9 +600,6 @@
|
|
|
834
600
|
width: 100%;
|
|
835
601
|
}
|
|
836
602
|
|
|
837
|
-
/* Title line + status/meta band (PageContainer `status`, godxjp-ui#255). The band sits on
|
|
838
|
-
* the title's baseline row at the token-owned gap and WRAPS under the title when the
|
|
839
|
-
* viewport (390px) or a long JA/VI title leaves no room — never clips, never overlaps. */
|
|
840
603
|
.ui-page-header-title-row {
|
|
841
604
|
display: flex;
|
|
842
605
|
flex-wrap: wrap;
|
|
@@ -865,24 +628,10 @@
|
|
|
865
628
|
overflow-wrap: anywhere;
|
|
866
629
|
}
|
|
867
630
|
|
|
868
|
-
/* `headerScale="chrome"` — the page's top row is CHROME (a chat channel, a mail thread, an IDE
|
|
869
|
-
* tab), not a document title, so the `<h1>` takes the body type step. The ELEMENT is untouched:
|
|
870
|
-
* still an `<h1>`, still the page's one level-1 heading, because the screen-reader outline is
|
|
871
|
-
* what a page is navigated by — only the type step moves, and only through a token.
|
|
872
|
-
*
|
|
873
|
-
* The compound selector's specificity (0,2,0) is load-bearing, not decoration: the 720px block
|
|
874
|
-
* below re-declares `.ui-page-title` (0,1,0) at the COMPACT DOCUMENT step, which is h2 (18px) —
|
|
875
|
-
* BIGGER than this. Without out-ranking it, a chrome header would swell on a phone, i.e. exactly
|
|
876
|
-
* where the height matters most. The responsive step itself is untouched and still governs every
|
|
877
|
-
* document-scaled page. */
|
|
878
631
|
.ui-page-container[data-header-scale="chrome"] .ui-page-title {
|
|
879
632
|
font-size: var(--page-title-font-size-chrome);
|
|
880
633
|
}
|
|
881
634
|
|
|
882
|
-
/* Compact viewports use a responsive type step, not compact density: body/control text and
|
|
883
|
-
* touch targets remain readable while page chrome stops competing with primary actions. A
|
|
884
|
-
* single Flex action group must wrap even when the consumer omitted `wrap`, because clipping
|
|
885
|
-
* header actions makes functionality unreachable. */
|
|
886
635
|
@media (max-width: 720px) {
|
|
887
636
|
.ui-page-container {
|
|
888
637
|
--space-page-active-x: var(--space-page-compact-x);
|
|
@@ -904,13 +653,6 @@
|
|
|
904
653
|
}
|
|
905
654
|
}
|
|
906
655
|
|
|
907
|
-
/* `headerLayout="responsive-inline"` (gh#231): below the 640px step the DEFAULT arrangement
|
|
908
|
-
* drops `extra` onto its own full-width line under the subtitle, which pushes a compact header
|
|
909
|
-
* control (a member search, one primary action) off the title row on a 390px screen. This
|
|
910
|
-
* arrangement keeps it beside the title band at a token-owned measure instead, and lets the
|
|
911
|
-
* title/subtitle wrap into the space that is left. Scoped to the compact range only, so at
|
|
912
|
-
* >=640px both arrangements resolve to the identical row below — and a page that never sets
|
|
913
|
-
* `headerLayout` never matches these rules at all. */
|
|
914
656
|
@media (max-width: 639.98px) {
|
|
915
657
|
.ui-page-header[data-layout="responsive-inline"] .ui-page-header-row {
|
|
916
658
|
flex-direction: row;
|
|
@@ -940,20 +682,6 @@
|
|
|
940
682
|
gap: var(--space-inline-lg);
|
|
941
683
|
}
|
|
942
684
|
|
|
943
|
-
/* `extra` sizes to its content but STAYS SHRINKABLE (gh#300). `width: auto` releases the
|
|
944
|
-
* base rule's full-width stretch so a one-button header hugs the end edge; the historical
|
|
945
|
-
* `flex-shrink: 0` next to it then froze the box at the action group's max-content width,
|
|
946
|
-
* which is unbounded — an admin header with 10–13 buttons asks for ~1594px inside a 1408px
|
|
947
|
-
* content column. Because the box could not shrink, its own `flex-wrap: wrap` never had a
|
|
948
|
-
* narrower width to wrap into, the whole deficit was charged to `.ui-page-header-heading`
|
|
949
|
-
* (`min-w-0`, so it collapses to 0), and the `<h1>` wrapped one CJK character per line while
|
|
950
|
-
* the action group still overflowed the page. The <=720px escape hatch above
|
|
951
|
-
* (`.ui-page-header-extra > .ui-flex { max-width: 100% }`) could not help either: a
|
|
952
|
-
* percentage resolved against a fit-content parent is circular.
|
|
953
|
-
*
|
|
954
|
-
* `flex-shrink: 1` + `min-inline-size: 0` keep the max-content base size (nothing moves on a
|
|
955
|
-
* header that already fits) but let the row charge the overflow to `extra` too, so the wrap
|
|
956
|
-
* it already declares finally engages and the title keeps a readable measure. */
|
|
957
685
|
.ui-page-header-extra {
|
|
958
686
|
width: auto;
|
|
959
687
|
flex-shrink: 1;
|
|
@@ -961,21 +689,6 @@
|
|
|
961
689
|
justify-content: flex-end;
|
|
962
690
|
}
|
|
963
691
|
|
|
964
|
-
/* `headerScale="chrome"`, second consequence of the SAME fact. The row's `align-items:
|
|
965
|
-
* flex-start` above is right for a DOCUMENT: a `Save`/`Publish` group belongs on the first
|
|
966
|
-
* line of a tall `<h1>`, not floating beside its second line. A chrome band has no tall
|
|
967
|
-
* heading to align to — it is one bar, and its actions sit on the bar's middle. Measured
|
|
968
|
-
* against the reference chat screen the difference was 8.65px: the header row is 45.3px, the
|
|
969
|
-
* icon buttons 28px, so top-packing put their centres at y=14 while the title block's centre
|
|
970
|
-
* sat at y=22.65. That gap is what reads, correctly, as "the icons are not centred".
|
|
971
|
-
*
|
|
972
|
-
* `align-self` on the EXTRA box, not `align-items` on the row: the heading keeps the stretch
|
|
973
|
-
* it has today (nothing about the title's own alignment is in question), and the two axes stay
|
|
974
|
-
* independent. Scoped INSIDE the 640px block on purpose — below it the row is a COLUMN, where
|
|
975
|
-
* the cross axis is horizontal and this same declaration would centre `extra` sideways and
|
|
976
|
-
* release its full-width stretch. `headerLayout="responsive-inline"` runs its own row
|
|
977
|
-
* arrangement under 640px and keeps its own flex-start; that arrangement exists to hold ONE
|
|
978
|
-
* compact control beside a wrapping title band, which is the document case again. */
|
|
979
692
|
.ui-page-container[data-header-scale="chrome"] .ui-page-header-extra {
|
|
980
693
|
align-self: center;
|
|
981
694
|
}
|
|
@@ -987,28 +700,10 @@
|
|
|
987
700
|
margin-top: var(--space-1);
|
|
988
701
|
}
|
|
989
702
|
|
|
990
|
-
/* `headerScale="chrome"`, third consequence of the SAME fact: if the top row is chrome, the line
|
|
991
|
-
* under it is a caption ON that chrome (a channel purpose, a mail preview), not a document's
|
|
992
|
-
* standfirst. The document step is --font-size-base — the identical step the chrome TITLE takes —
|
|
993
|
-
* so a chrome header was shipping a title and its subtitle at one size, which is not a hierarchy.
|
|
994
|
-
* Same size is also not the expensive half: the step drives the LINE BOX (14px × 1.7 = 23.8px vs
|
|
995
|
-
* 11px × 1.7 = 18.9px), and a band that exists to give the transcript room was spending 5px of it
|
|
996
|
-
* on a caption. Type only — no line-height, no colour, no weight: `--line-height-body` still owns
|
|
997
|
-
* the rhythm so a wrapped JA/VI purpose line stays readable at the smaller step.
|
|
998
|
-
*
|
|
999
|
-
* The specificity is load-bearing exactly as it is on the title above: the 720px block declares
|
|
1000
|
-
* `.ui-page-header .ui-page-subtitle` (0,2,0) — already compounded, unlike the bare `.ui-page-title`
|
|
1001
|
-
* — so this rule out-ranks it only because the container attribute makes it (0,3,0). Without that
|
|
1002
|
-
* the compact step would win below 720px and a chrome subtitle would step UP to 14px on a phone,
|
|
1003
|
-
* i.e. exactly where the band's height is worth the most. */
|
|
1004
703
|
.ui-page-container[data-header-scale="chrome"] .ui-page-subtitle {
|
|
1005
704
|
font-size: var(--page-subtitle-font-size-chrome);
|
|
1006
705
|
}
|
|
1007
706
|
|
|
1008
|
-
/* `headerLoading` placeholders — the real `<h1>`/`<p>` wearing the shared `.ui-skeleton-block`
|
|
1009
|
-
* skin, so the pending band occupies the same rhythm the settled band will. `max-inline-size:
|
|
1010
|
-
* 100%` keeps them inside a 390px column; the block size replaces the line box the (sr-only)
|
|
1011
|
-
* text no longer fills. */
|
|
1012
707
|
.ui-page-title-placeholder {
|
|
1013
708
|
inline-size: var(--page-title-placeholder-measure);
|
|
1014
709
|
max-inline-size: 100%;
|
|
@@ -1021,27 +716,19 @@
|
|
|
1021
716
|
min-block-size: var(--page-subtitle-placeholder-block-size);
|
|
1022
717
|
}
|
|
1023
718
|
|
|
1024
|
-
/* Default: content-height + top-packed. The container's min-height:100% keeps
|
|
1025
|
-
* the page background spanning the viewport-tall shell, but the body itself is
|
|
1026
|
-
* NOT stretched — so short pages no longer leave a jarring void below their
|
|
1027
|
-
* content (gh#103). Opt into a viewport-filling body with `fill`. */
|
|
1028
719
|
.ui-page-body {
|
|
1029
720
|
min-width: 0;
|
|
1030
721
|
container-type: inline-size;
|
|
1031
722
|
}
|
|
1032
723
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
* items at the two ends, outside the scroller, so transcript content can never
|
|
1042
|
-
* travel underneath them. Sticky bands cannot give that — sticky keeps the
|
|
1043
|
-
* content flowing beneath the pinned box, which is exactly the half-sliced
|
|
1044
|
-
* message line the chat page was showing. */
|
|
724
|
+
.ui-page-body > * + * {
|
|
725
|
+
margin-block-start: var(--page-body-gap);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.ui-page-body > * + * {
|
|
729
|
+
margin-block-start: var(--page-body-gap);
|
|
730
|
+
}
|
|
731
|
+
|
|
1045
732
|
.ui-page-container--fill {
|
|
1046
733
|
flex: 1;
|
|
1047
734
|
min-height: 0;
|
|
@@ -1053,19 +740,6 @@
|
|
|
1053
740
|
overflow-y: auto;
|
|
1054
741
|
}
|
|
1055
742
|
|
|
1056
|
-
/* The THIRD chrome band, and until now the only one whose rule was not a knob. The header reads
|
|
1057
|
-
* --page-header-divider and the toolbar reads --page-toolbar-divider, but this line was a
|
|
1058
|
-
* hard-copied literal, so a page could not turn it off at all. Measured on a consumer chat
|
|
1059
|
-
* screen: the composer lives in this slot and is itself a bordered Card, so the full-width rule
|
|
1060
|
-
* landed straight above it as a SECOND line — a pixel diff against the design caught a 100%-wide
|
|
1061
|
-
* rule at y=701 that the design does not have.
|
|
1062
|
-
*
|
|
1063
|
-
* Unlike the other two, the default here is a rule rather than silence, and that is deliberate:
|
|
1064
|
-
* `footer` is the shared slot a form's Save/Cancel bar lands in, where the line separating the
|
|
1065
|
-
* actions from the content is the behaviour every existing page already depends on. The literal
|
|
1066
|
-
* simply moves into the fallback, so `--page-footer-divider` unset is byte-identical to what
|
|
1067
|
-
* this rule drew before, and `--page-footer-divider: none` is the opt-OUT the chat composer
|
|
1068
|
-
* needed. */
|
|
1069
743
|
.ui-page-footer {
|
|
1070
744
|
display: flex;
|
|
1071
745
|
flex-direction: column-reverse;
|
|
@@ -1143,7 +817,6 @@
|
|
|
1143
817
|
.ui-empty-state-description {
|
|
1144
818
|
font-size: var(--font-size-base);
|
|
1145
819
|
color: hsl(var(--muted-foreground));
|
|
1146
|
-
/* Measure knob (rule #45) — default 28rem, retunable per service/locale. */
|
|
1147
820
|
max-width: var(--empty-state-description-max-width);
|
|
1148
821
|
}
|
|
1149
822
|
|
|
@@ -1154,8 +827,7 @@
|
|
|
1154
827
|
inline-size: var(--empty-state-icon-size);
|
|
1155
828
|
block-size: var(--empty-state-icon-size);
|
|
1156
829
|
border-radius: var(--radius-pill);
|
|
1157
|
-
|
|
1158
|
-
* --muted-foreground roles at the call site (re-resolves under a scoped theme). */
|
|
830
|
+
|
|
1159
831
|
background-color: var(--empty-state-icon-tint, hsl(var(--muted)));
|
|
1160
832
|
color: var(--empty-state-icon-foreground, hsl(var(--muted-foreground)));
|
|
1161
833
|
}
|
|
@@ -1165,8 +837,6 @@
|
|
|
1165
837
|
block-size: var(--empty-state-icon-glyph-size);
|
|
1166
838
|
}
|
|
1167
839
|
|
|
1168
|
-
/* tone — set the medallion foreground + tint from the matching role token, so a consumer never
|
|
1169
|
-
* hand-rolls a `.ui-success-state` class. `muted` (default) keeps the neutral role fallback. */
|
|
1170
840
|
.ui-empty-state[data-tone="success"] {
|
|
1171
841
|
--empty-state-icon-foreground: hsl(var(--success));
|
|
1172
842
|
--empty-state-icon-tint: hsl(var(--success) / 0.12);
|
|
@@ -1200,11 +870,7 @@
|
|
|
1200
870
|
}
|
|
1201
871
|
|
|
1202
872
|
@layer components {
|
|
1203
|
-
|
|
1204
|
-
* `flex-wrap: wrap` split the range / size-changer / page strip across rows). The number strip
|
|
1205
|
-
* (`.ui-pagination-list`) absorbs any overflow by scrolling horizontally rather than wrapping; a
|
|
1206
|
-
* genuinely narrow viewport uses the INTENTIONAL compact form via the `simple` prop (Prev · n/N ·
|
|
1207
|
-
* Next, no page buttons) — not browser-driven wrapping. */
|
|
873
|
+
|
|
1208
874
|
.ui-pagination {
|
|
1209
875
|
display: flex;
|
|
1210
876
|
flex-wrap: nowrap;
|
|
@@ -1214,8 +880,6 @@
|
|
|
1214
880
|
gap: var(--pagination-gap);
|
|
1215
881
|
}
|
|
1216
882
|
|
|
1217
|
-
/* Total / range label sits at the start and truncates rather than pushing the controls onto a
|
|
1218
|
-
* second line — the nav row stays one line at every width (gh#153). */
|
|
1219
883
|
.ui-pagination-total {
|
|
1220
884
|
min-width: 0;
|
|
1221
885
|
margin-inline-end: auto;
|
|
@@ -1234,8 +898,7 @@
|
|
|
1234
898
|
|
|
1235
899
|
.ui-pagination-size-trigger {
|
|
1236
900
|
width: var(--pagination-size-width);
|
|
1237
|
-
|
|
1238
|
-
* overrides via className if it wants smaller. Shared with the page buttons. */
|
|
901
|
+
|
|
1239
902
|
height: var(--control-height);
|
|
1240
903
|
}
|
|
1241
904
|
|
|
@@ -1244,8 +907,7 @@
|
|
|
1244
907
|
min-width: 0;
|
|
1245
908
|
align-items: center;
|
|
1246
909
|
gap: var(--pagination-item-gap);
|
|
1247
|
-
|
|
1248
|
-
* the desktop bar clean; the strip stays swipeable on touch. */
|
|
910
|
+
|
|
1249
911
|
overflow-x: auto;
|
|
1250
912
|
scrollbar-width: none;
|
|
1251
913
|
}
|
|
@@ -1270,13 +932,6 @@
|
|
|
1270
932
|
color: hsl(var(--muted-foreground));
|
|
1271
933
|
}
|
|
1272
934
|
|
|
1273
|
-
/* The button GROWS with its label instead of being a rigid square. `width: var(--control-height)`
|
|
1274
|
-
* capped every control at one control-height, so a five-figure page number spilled out of its
|
|
1275
|
-
* own box (measured: `12345678` wanted 59.3px of text inside a 30px content box; `1043` cleared
|
|
1276
|
-
* a 32px box by 0.3px and clipped outright at the consumer's 29px density). Ant Design, MUI and
|
|
1277
|
-
* shadcn all size the same way — a min-width for the rhythm, padding for the overflow — so the
|
|
1278
|
-
* one- and two-digit buttons keep the square look while longer ones widen. Height stays pinned so
|
|
1279
|
-
* the row never changes rhythm. */
|
|
1280
935
|
.ui-pagination-link {
|
|
1281
936
|
display: inline-flex;
|
|
1282
937
|
min-width: var(--control-height);
|
|
@@ -1290,11 +945,6 @@
|
|
|
1290
945
|
text-decoration: none;
|
|
1291
946
|
}
|
|
1292
947
|
|
|
1293
|
-
/* Keyboard focus: drawn by the single-source rule in styles/focus-ring.css
|
|
1294
|
-
* (`.ui-pagination-link` is in its shadow-form list). Before that, focus here was
|
|
1295
|
-
* left to the browser, which paints its OWN ring — measured
|
|
1296
|
-
* `outline: rgb(0, 95, 204) auto 1px`, a blue belonging to no theme. */
|
|
1297
|
-
|
|
1298
948
|
.ui-pagination-link[data-active="true"] {
|
|
1299
949
|
border-color: hsl(var(--ring) / 0.5);
|
|
1300
950
|
background: hsl(var(--primary));
|
|
@@ -1343,9 +993,6 @@
|
|
|
1343
993
|
}
|
|
1344
994
|
}
|
|
1345
995
|
|
|
1346
|
-
/* Toolbar `sticky` (#197): pin the filter strip while the list scrolls beneath it.
|
|
1347
|
-
* `inset-block-start` (not `top`) keeps it RTL/vertical-writing-mode safe; the fill only
|
|
1348
|
-
* paints in this pinned variant so a non-sticky toolbar stays transparent chrome. */
|
|
1349
996
|
.ui-toolbar-sticky {
|
|
1350
997
|
position: sticky;
|
|
1351
998
|
inset-block-start: var(--filter-bar-sticky-offset);
|
|
@@ -1382,13 +1029,6 @@
|
|
|
1382
1029
|
font-size: var(--filter-label-font-size);
|
|
1383
1030
|
}
|
|
1384
1031
|
|
|
1385
|
-
/* Toolbar/FilterBar `overflow="scroll"` (#216) — one bounded row that scrolls INLINE instead of
|
|
1386
|
-
* wrapping onto extra rows. Below 640px the strip still stacks (the `wrap` base rules above own
|
|
1387
|
-
* that): a 390px viewport must never hide a filter behind a horizontal scroll it cannot see.
|
|
1388
|
-
* `overflow-x` matches the rest of the package's scroll regions (.ui-data-table-scroll, tabs
|
|
1389
|
-
* list) and the browser already mirrors its scroll origin under `dir="rtl"`; the INSET/margin
|
|
1390
|
-
* properties below stay logical. No `tabindex` is needed — every group holds a focusable
|
|
1391
|
-
* control, so the region is keyboard-reachable (axe scrollable-region-focusable / WCAG 2.1.1). */
|
|
1392
1032
|
@media (min-width: 640px) {
|
|
1393
1033
|
.ui-toolbar[data-overflow="scroll"] {
|
|
1394
1034
|
flex-wrap: nowrap;
|
|
@@ -1402,20 +1042,16 @@
|
|
|
1402
1042
|
flex: 0 0 auto;
|
|
1403
1043
|
}
|
|
1404
1044
|
|
|
1405
|
-
/* The clear action stays parked at the inline end of the VIEWPORT of the scroll region, not at
|
|
1406
|
-
* the end of its scroll width — `margin-inline-start: auto` would push it past the fold and
|
|
1407
|
-
* make "clear all" unreachable without scrolling. */
|
|
1408
1045
|
.ui-toolbar[data-overflow="scroll"] > .ui-toolbar-clear {
|
|
1409
1046
|
position: sticky;
|
|
1410
1047
|
inset-inline-end: 0;
|
|
1411
1048
|
margin-inline-start: auto;
|
|
1412
1049
|
flex: 0 0 auto;
|
|
1413
|
-
|
|
1050
|
+
|
|
1414
1051
|
z-index: 1;
|
|
1415
1052
|
background: hsl(var(--filter-bar-sticky-background, var(--background)));
|
|
1416
1053
|
}
|
|
1417
1054
|
|
|
1418
|
-
/* Typed-model reset+actions cluster gets the same viewport-pinned treatment (gh#258). */
|
|
1419
1055
|
.ui-toolbar[data-overflow="scroll"] > .ui-filter-bar-end {
|
|
1420
1056
|
position: sticky;
|
|
1421
1057
|
inset-inline-end: 0;
|
|
@@ -1426,11 +1062,6 @@
|
|
|
1426
1062
|
}
|
|
1427
1063
|
}
|
|
1428
1064
|
|
|
1429
|
-
/* ── FilterBar typed model (gh#258) ─────────────────────────────────────────────
|
|
1430
|
-
* Canonical model layout: strip (search → filters → children → reset → actions) →
|
|
1431
|
-
* applied-chips row → result-count/error line. Geometry is entirely token-owned
|
|
1432
|
-
* (`--filter-bar-*`); stacking below 640px reuses the `.ui-toolbar` base rules, so
|
|
1433
|
-
* 1440/1024/390 behave identically to the composition form. */
|
|
1434
1065
|
.ui-filter-bar {
|
|
1435
1066
|
display: flex;
|
|
1436
1067
|
flex-direction: column;
|
|
@@ -1449,8 +1080,6 @@
|
|
|
1449
1080
|
width: 100%;
|
|
1450
1081
|
}
|
|
1451
1082
|
|
|
1452
|
-
/* Inside the end cluster the clear button gives up its own auto margin — the CLUSTER is what
|
|
1453
|
-
* parks at the inline end, so reset and actions stay adjacent in the keyboard/DOM order. */
|
|
1454
1083
|
.ui-filter-bar-end .ui-toolbar-clear {
|
|
1455
1084
|
margin-inline-start: 0;
|
|
1456
1085
|
}
|
|
@@ -1500,20 +1129,6 @@
|
|
|
1500
1129
|
color: hsl(var(--destructive));
|
|
1501
1130
|
}
|
|
1502
1131
|
|
|
1503
|
-
/* ── LegalDocumentShell (#222) ─────────────────────────────────────────────────
|
|
1504
|
-
* The long-form legal/policy document surface: a readable, measure-capped document
|
|
1505
|
-
* column with a STICKY table-of-contents rail beside it. Every value reads a
|
|
1506
|
-
* `--legal-document-*` component token, so a service re-tunes the measure, the rail
|
|
1507
|
-
* and the section rhythm from theme.css with zero consumer `.legal-*` CSS.
|
|
1508
|
-
*
|
|
1509
|
-
* It OWNS its query container (`container: legal-document / inline-size`, the SplitPane
|
|
1510
|
-
* precedent — gh#165), so the one-column ⇄ two-column decision comes from the SHELL's
|
|
1511
|
-
* own available width, not the viewport: dropped into a narrow pane on a 1440px screen
|
|
1512
|
-
* it correctly stays single-column.
|
|
1513
|
-
*
|
|
1514
|
-
* DOM order is header → rail (contents) → sections → footer, which is exactly the
|
|
1515
|
-
* mobile reading order; the desktop branch only re-places those boxes into a grid.
|
|
1516
|
-
*/
|
|
1517
1132
|
.ui-legal-document-scope {
|
|
1518
1133
|
container: legal-document / inline-size;
|
|
1519
1134
|
}
|
|
@@ -1544,9 +1159,6 @@
|
|
|
1544
1159
|
color: hsl(var(--legal-document-meta-foreground, var(--muted-foreground)));
|
|
1545
1160
|
}
|
|
1546
1161
|
|
|
1547
|
-
/* Long-form prose wrapping that holds in JA (kinsoku via `line-break: strict`), in EN
|
|
1548
|
-
* (long compounds / URLs via `overflow-wrap: break-word`) and in VI (diacritic-heavy
|
|
1549
|
-
* words must NOT be broken mid-word — `word-break: normal` keeps that true). */
|
|
1550
1162
|
.ui-legal-document-summary,
|
|
1551
1163
|
.ui-legal-document-section-body {
|
|
1552
1164
|
min-width: 0;
|
|
@@ -1592,8 +1204,6 @@
|
|
|
1592
1204
|
min-height: 0;
|
|
1593
1205
|
}
|
|
1594
1206
|
|
|
1595
|
-
/* REAL anchors — `href="#<section id>"` — so the contents list is keyboard-reachable,
|
|
1596
|
-
* middle-clickable and deep-linkable even before JS boots. */
|
|
1597
1207
|
.ui-legal-document-toc-link {
|
|
1598
1208
|
display: block;
|
|
1599
1209
|
padding: var(--legal-document-toc-item-padding);
|
|
@@ -1613,9 +1223,6 @@
|
|
|
1613
1223
|
background: hsl(var(--legal-document-toc-active-background, var(--accent)) / 0.6);
|
|
1614
1224
|
}
|
|
1615
1225
|
|
|
1616
|
-
/* Active entry: colour + a leading marker + a heavier weight, so the state survives a
|
|
1617
|
-
* monochrome / high-contrast rendering (never colour-only, WCAG 1.4.1). The anchor also
|
|
1618
|
-
* carries `aria-current="location"`. */
|
|
1619
1226
|
.ui-legal-document-toc-link[data-active] {
|
|
1620
1227
|
color: hsl(var(--legal-document-toc-active-foreground, var(--foreground)));
|
|
1621
1228
|
background: hsl(var(--legal-document-toc-active-background, var(--accent)));
|
|
@@ -1631,9 +1238,6 @@
|
|
|
1631
1238
|
max-width: var(--legal-document-measure-max-width);
|
|
1632
1239
|
}
|
|
1633
1240
|
|
|
1634
|
-
/* `scroll-margin-block-start` (logical, so it survives vertical writing modes) is what
|
|
1635
|
-
* makes a hash jump AND a programmatic `scrollIntoView` land BELOW sticky chrome — no
|
|
1636
|
-
* JS offset arithmetic, no consumer CSS. */
|
|
1637
1241
|
.ui-legal-document-section {
|
|
1638
1242
|
display: flex;
|
|
1639
1243
|
flex-direction: column;
|
|
@@ -1642,8 +1246,6 @@
|
|
|
1642
1246
|
scroll-margin-block-start: var(--legal-document-scroll-offset);
|
|
1643
1247
|
}
|
|
1644
1248
|
|
|
1645
|
-
/* The section is `tabIndex={-1}` and receives focus when its contents anchor is
|
|
1646
|
-
* activated, so the focus ring must be VISIBLE (WCAG 2.4.7 / 2.4.11). */
|
|
1647
1249
|
.ui-legal-document-footer {
|
|
1648
1250
|
display: flex;
|
|
1649
1251
|
flex-wrap: wrap;
|
|
@@ -1654,15 +1256,6 @@
|
|
|
1654
1256
|
border-block-start: var(--legal-document-footer-border);
|
|
1655
1257
|
}
|
|
1656
1258
|
|
|
1657
|
-
/* Two-column mode (shell ≥56rem — a 1024px and a 1440px viewport both qualify): the
|
|
1658
|
-
* rail is PINNED beside a measure-capped document column and spans the header/body/
|
|
1659
|
-
* footer rows, so it stays in view for the whole scroll. Logical grid placement means
|
|
1660
|
-
* column 1 is the START side, i.e. it mirrors correctly under `dir="rtl"`.
|
|
1661
|
-
*
|
|
1662
|
-
* Below 56rem (the 390×844 case) NOTHING here applies: the shell stays a single column
|
|
1663
|
-
* and the rail is a STATIC compact contents block between the document header and the
|
|
1664
|
-
* first section — deliberately not sticky and not height-capped, so a phone never loses
|
|
1665
|
-
* vertical space to pinned chrome. */
|
|
1666
1259
|
@container legal-document (min-width: 56rem) {
|
|
1667
1260
|
.ui-legal-document {
|
|
1668
1261
|
grid-template-columns:
|
|
@@ -1699,18 +1292,7 @@
|
|
|
1699
1292
|
}
|
|
1700
1293
|
|
|
1701
1294
|
@layer components {
|
|
1702
|
-
|
|
1703
|
-
* `hideBelow` / `hideFrom` are the ONE public way to drop a layout region at a breakpoint step.
|
|
1704
|
-
* Without them a public header cannot hide its anchor navigation below the tablet step, or show
|
|
1705
|
-
* a compact-only affordance, without a page-local media query — which is exactly the API-first
|
|
1706
|
-
* violation gh#252 reports. The steps are the package's canonical breakpoint scale, the same
|
|
1707
|
-
* values `--master-detail-collapse-below` uses (sm 40rem · md 48rem · lg 64rem · xl 80rem); a
|
|
1708
|
-
* media query cannot read a `var()`, so the step values are the tokenized scale written out.
|
|
1709
|
-
*
|
|
1710
|
-
* Both props default to `undefined`, so NO `data-hide-*` attribute is emitted and none of these
|
|
1711
|
-
* selectors can match — every existing Flex is untouched (the gh#231 inert-default contract).
|
|
1712
|
-
* `display: none` removes the region from the a11y tree too, which is the point: the destination
|
|
1713
|
-
* must stay reachable from another region (the footer nav) at that width. */
|
|
1295
|
+
|
|
1714
1296
|
@media (width < 40rem) {
|
|
1715
1297
|
.ui-flex[data-hide-below="sm"] {
|
|
1716
1298
|
display: none;
|