@krizaka/ui 3.29.0 → 3.31.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 +4 -8
- package/dist/action-bar.js +3 -3
- package/dist/action-bar.js.map +1 -1
- package/dist/argument-card.d.ts +1 -1
- package/dist/argument-card.js +2 -2
- package/dist/argument-card.js.map +1 -1
- package/dist/blocks.css +19 -60
- package/dist/{button-BmCrLCq7.d.ts → button-Cm9pFH3P.d.ts} +10 -9
- package/dist/button.d.ts +11 -10
- package/dist/button.js +1 -1
- package/dist/captcha.js +1 -1
- package/dist/card-grid.d.ts +5 -12
- package/dist/card-grid.js +4 -12
- package/dist/card-grid.js.map +1 -1
- package/dist/card.d.ts +7 -7
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/{chunk-OUWPBSSL.js → chunk-2AJ4NJUF.js} +8 -9
- package/dist/chunk-2AJ4NJUF.js.map +1 -0
- package/dist/{chunk-MQPC3P4T.js → chunk-2DGLWTPS.js} +3 -3
- package/dist/chunk-2DGLWTPS.js.map +1 -0
- package/dist/{chunk-DZU6RMYD.js → chunk-3DHNN5V7.js} +15 -9
- package/dist/chunk-3DHNN5V7.js.map +1 -0
- package/dist/{chunk-TFJDIOFK.js → chunk-4E5IUZHR.js} +3 -3
- package/dist/chunk-4E5IUZHR.js.map +1 -0
- package/dist/{chunk-HAQWKORZ.js → chunk-4JO5GSSD.js} +1 -37
- package/dist/chunk-4JO5GSSD.js.map +1 -0
- package/dist/{chunk-APNIVNIX.js → chunk-IBDTAMPF.js} +3 -4
- package/dist/chunk-IBDTAMPF.js.map +1 -0
- package/dist/{chunk-LYOPPBMC.js → chunk-KIBICUZK.js} +2 -2
- package/dist/{chunk-HBZPJ3EP.js → chunk-NJFCSVPT.js} +2 -2
- package/dist/{chunk-KM6VAKIE.js → chunk-RE5WJ4QP.js} +2 -2
- package/dist/chunk-RE5WJ4QP.js.map +1 -0
- package/dist/{chunk-CHOJUCVE.js → chunk-SJS6RJIT.js} +15 -9
- package/dist/chunk-SJS6RJIT.js.map +1 -0
- package/dist/{chunk-6ZYRUOTA.js → chunk-UV4HBVH7.js} +15 -9
- package/dist/chunk-UV4HBVH7.js.map +1 -0
- package/dist/command.js +2 -2
- package/dist/confirm-button.d.ts +1 -1
- package/dist/confirm-button.js +1 -1
- package/dist/continue-prompt.js +14 -8
- package/dist/continue-prompt.js.map +1 -1
- package/dist/data-table.js +2 -2
- package/dist/dialog.js +2 -2
- package/dist/engine-flow.d.ts +25 -2
- package/dist/engine-flow.js +27 -6
- package/dist/engine-flow.js.map +1 -1
- package/dist/force-update-screen.js +2 -2
- package/dist/force-update-screen.js.map +1 -1
- package/dist/genui.js +3 -3
- package/dist/glow-card.d.ts +5 -4
- package/dist/glow-card.js +2 -8
- package/dist/glow-card.js.map +1 -1
- package/dist/image-cropper.js +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/iso.d.ts +1 -1
- package/dist/iso.js.map +1 -1
- package/dist/landscape.js +4 -4
- package/dist/landscape.js.map +1 -1
- package/dist/link-card.d.ts +3 -6
- package/dist/link-card.js +1 -1
- package/dist/maintenance-screen.js +1 -1
- package/dist/map.d.ts +50 -5
- package/dist/map.js +202 -66
- package/dist/map.js.map +1 -1
- package/dist/match-dialog.js +1 -1
- package/dist/media-gallery.js +1 -1
- package/dist/media-gallery.js.map +1 -1
- package/dist/media-grid.js +2 -2
- package/dist/motion.css +52 -122
- package/dist/motion.d.ts +2 -2
- package/dist/motion.js +1 -1
- package/dist/native/map.js +64 -64
- package/dist/native/map.js.map +1 -1
- package/dist/native.js +30 -30
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.d.ts +12 -3
- package/dist/nav-bar.js +19 -12
- package/dist/nav-bar.js.map +1 -1
- package/dist/pointer-light.d.ts +6 -9
- package/dist/pointer-light.js +9 -5
- package/dist/pointer-light.js.map +1 -1
- package/dist/radio-group.js +2 -2
- package/dist/radio-group.js.map +1 -1
- package/dist/service-card.js +1 -1
- package/dist/service-card.js.map +1 -1
- package/dist/site-footer.d.ts +1 -1
- package/dist/site-footer.js +2 -2
- package/dist/{sites-CHp_vELn.d.ts → sites-B4prB793.d.ts} +2 -2
- package/dist/sites.d.ts +2 -2
- package/dist/sites.js +6 -7
- package/dist/sites.js.map +1 -1
- package/dist/slider.js +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/story.css +5 -12
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +1 -1
- package/dist/update-prompt.js +2 -2
- package/dist/update-prompt.js.map +1 -1
- package/dist/vertical-video.js +1 -1
- package/dist/video-player.js +2 -2
- package/dist/video-player.js.map +1 -1
- package/package.json +5 -5
- package/registry/action-bar.json +1 -1
- package/registry/app-shell.json +4 -4
- package/registry/argument-card.json +7 -16
- package/registry/auth-layout.json +4 -4
- package/registry/button.json +20 -20
- package/registry/card-grid.json +21 -24
- package/registry/card.json +7 -7
- package/registry/component-page.json +2 -2
- package/registry/confirm-button.json +6 -6
- package/registry/contact-page.json +2 -2
- package/registry/cover-hero.json +3 -3
- package/registry/dashboard-layout.json +2 -2
- package/registry/detail-layout.json +4 -4
- package/registry/docs-hub-page.json +2 -2
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +74 -3
- package/registry/examples/app-shell/admin-console.js +1 -1
- package/registry/examples/app-shell/admin-console.tsx +1 -1
- package/registry/examples/app-shell/basic.js +1 -1
- package/registry/examples/app-shell/basic.tsx +1 -1
- package/registry/examples/app-shell/desktop.js +1 -1
- package/registry/examples/app-shell/desktop.tsx +1 -1
- package/registry/examples/app-shell/orochia-studio.js +1 -1
- package/registry/examples/app-shell/orochia-studio.tsx +1 -1
- package/registry/examples/argument-card/hover-quiet.js +1 -1
- package/registry/examples/argument-card/hover-quiet.tsx +1 -1
- package/registry/examples/auth-layout/onboarding.js +2 -2
- package/registry/examples/auth-layout/onboarding.tsx +2 -2
- package/registry/examples/auth-layout/sign-in.js +2 -2
- package/registry/examples/auth-layout/sign-in.tsx +2 -2
- package/registry/examples/auth-layout/verify.js +1 -1
- package/registry/examples/auth-layout/verify.tsx +1 -1
- package/registry/examples/avatar/native/presence.dark.png +0 -0
- package/registry/examples/avatar/native/presence.light.png +0 -0
- package/registry/examples/badge/native/tones.dark.png +0 -0
- package/registry/examples/badge/native/tones.light.png +0 -0
- package/registry/examples/button/native/disabled.dark.png +0 -0
- package/registry/examples/button/native/disabled.light.png +0 -0
- package/registry/examples/button/native/gradient.dark.png +0 -0
- package/registry/examples/button/native/gradient.light.png +0 -0
- package/registry/examples/button/native/icon-only.dark.png +0 -0
- package/registry/examples/button/native/icon-only.light.png +0 -0
- package/registry/examples/button/native/icon.dark.png +0 -0
- package/registry/examples/button/native/icon.light.png +0 -0
- package/registry/examples/button/native/loading.dark.png +0 -0
- package/registry/examples/button/native/loading.light.png +0 -0
- package/registry/examples/button/native/primary.dark.png +0 -0
- package/registry/examples/button/native/primary.light.png +0 -0
- package/registry/examples/button/native/sizes.dark.png +0 -0
- package/registry/examples/button/native/sizes.light.png +0 -0
- package/registry/examples/button/signature.js +2 -2
- package/registry/examples/button/signature.tsx +4 -4
- package/registry/examples/card/interactive-link.tsx +1 -1
- package/registry/examples/card/native/body.dark.png +0 -0
- package/registry/examples/card/native/body.light.png +0 -0
- package/registry/examples/card/native/default.dark.png +0 -0
- package/registry/examples/card/native/default.light.png +0 -0
- package/registry/examples/card/native/elevated.dark.png +0 -0
- package/registry/examples/card/native/elevated.light.png +0 -0
- package/registry/examples/card-grid/hover-quiet.js +1 -1
- package/registry/examples/card-grid/hover-quiet.tsx +1 -1
- package/registry/examples/chat/native/assistant.dark.png +0 -0
- package/registry/examples/chat/native/assistant.light.png +0 -0
- package/registry/examples/chat/native/conversation.dark.png +0 -0
- package/registry/examples/chat/native/conversation.light.png +0 -0
- package/registry/examples/chat/native/inbox.dark.png +0 -0
- package/registry/examples/chat/native/inbox.light.png +0 -0
- package/registry/examples/checkbox/native/terms.dark.png +0 -0
- package/registry/examples/checkbox/native/terms.light.png +0 -0
- package/registry/examples/chip/native/multiple.dark.png +0 -0
- package/registry/examples/chip/native/multiple.light.png +0 -0
- package/registry/examples/chip/native/single.dark.png +0 -0
- package/registry/examples/chip/native/single.light.png +0 -0
- package/registry/examples/chip/native/toggle.dark.png +0 -0
- package/registry/examples/chip/native/toggle.light.png +0 -0
- package/registry/examples/component-page/button.js +2 -2
- package/registry/examples/component-page/button.tsx +2 -2
- package/registry/examples/contact-page/team.js +1 -1
- package/registry/examples/contact-page/team.tsx +1 -1
- package/registry/examples/cover-hero/plain.js +1 -1
- package/registry/examples/cover-hero/plain.tsx +1 -1
- package/registry/examples/cover-hero/shop.js +1 -1
- package/registry/examples/cover-hero/shop.tsx +1 -1
- package/registry/examples/dashboard-layout/orazaka-eric.js +2 -2
- package/registry/examples/dashboard-layout/orazaka-eric.tsx +2 -2
- package/registry/examples/date-picker/native/calendar.dark.png +0 -0
- package/registry/examples/date-picker/native/calendar.light.png +0 -0
- package/registry/examples/date-picker/native/day-strip.dark.png +0 -0
- package/registry/examples/date-picker/native/day-strip.light.png +0 -0
- package/registry/examples/date-picker/native/month-year.dark.png +0 -0
- package/registry/examples/date-picker/native/month-year.light.png +0 -0
- package/registry/examples/date-picker/native/time-slots.dark.png +0 -0
- package/registry/examples/date-picker/native/time-slots.light.png +0 -0
- package/registry/examples/description-list/native/job-details.dark.png +0 -0
- package/registry/examples/description-list/native/job-details.light.png +0 -0
- package/registry/examples/detail-layout/checkout.js +1 -1
- package/registry/examples/detail-layout/checkout.tsx +1 -1
- package/registry/examples/detail-layout/job.js +1 -1
- package/registry/examples/detail-layout/job.tsx +1 -1
- package/registry/examples/detail-layout/video.js +2 -2
- package/registry/examples/detail-layout/video.tsx +2 -2
- package/registry/examples/dialog/native/sheet.dark.png +0 -0
- package/registry/examples/dialog/native/sheet.light.png +0 -0
- package/registry/examples/docs-hub-page/krizaka.js +1 -1
- package/registry/examples/docs-hub-page/krizaka.tsx +1 -1
- package/registry/examples/empty-state/library.js +1 -1
- package/registry/examples/empty-state/library.tsx +1 -1
- package/registry/examples/empty-state/native/default.dark.png +0 -0
- package/registry/examples/empty-state/native/default.light.png +0 -0
- package/registry/examples/fab/native/extended.dark.png +0 -0
- package/registry/examples/fab/native/extended.light.png +0 -0
- package/registry/examples/force-update-screen/native/default.dark.png +0 -0
- package/registry/examples/force-update-screen/native/default.light.png +0 -0
- package/registry/examples/form/native/amount.dark.png +0 -0
- package/registry/examples/form/native/amount.light.png +0 -0
- package/registry/examples/form/native/audience.dark.png +0 -0
- package/registry/examples/form/native/audience.light.png +0 -0
- package/registry/examples/form/native/profile.dark.png +0 -0
- package/registry/examples/form/native/profile.light.png +0 -0
- package/registry/examples/form/native/settings.dark.png +0 -0
- package/registry/examples/form/native/settings.light.png +0 -0
- package/registry/examples/form/native/sign-up.dark.png +0 -0
- package/registry/examples/form/native/sign-up.light.png +0 -0
- package/registry/examples/form/native/upload.dark.png +0 -0
- package/registry/examples/form/native/upload.light.png +0 -0
- package/registry/examples/glow-card/spotlight.tsx +1 -1
- package/registry/examples/goal-gauge/native/default.dark.png +0 -0
- package/registry/examples/goal-gauge/native/default.light.png +0 -0
- package/registry/examples/landing-page/krizaka.js +3 -3
- package/registry/examples/landing-page/krizaka.tsx +3 -3
- package/registry/examples/link-card/hover-quiet.js +1 -1
- package/registry/examples/link-card/hover-quiet.tsx +1 -1
- package/registry/examples/loader/native/block.dark.png +0 -0
- package/registry/examples/loader/native/block.light.png +0 -0
- package/registry/examples/map/native/location-picker.dark.png +0 -0
- package/registry/examples/map/native/location-picker.light.png +0 -0
- package/registry/examples/map/route.js +36 -0
- package/registry/examples/map/route.tsx +35 -0
- package/registry/examples/marketing-layout/pricing.js +1 -1
- package/registry/examples/marketing-layout/pricing.tsx +1 -1
- package/registry/examples/marketing-layout/product-page.js +1 -1
- package/registry/examples/marketing-layout/product-page.tsx +2 -2
- package/registry/examples/mascot-state/native/default.dark.png +0 -0
- package/registry/examples/mascot-state/native/default.light.png +0 -0
- package/registry/examples/mascots/native/crane.dark.png +0 -0
- package/registry/examples/mascots/native/crane.light.png +0 -0
- package/registry/examples/mascots/native/default.dark.png +0 -0
- package/registry/examples/mascots/native/default.light.png +0 -0
- package/registry/examples/mascots/native/lovebird.dark.png +0 -0
- package/registry/examples/mascots/native/lovebird.light.png +0 -0
- package/registry/examples/mascots/native/phoenix.dark.png +0 -0
- package/registry/examples/mascots/native/phoenix.light.png +0 -0
- package/registry/examples/match-dialog/native/its-a-match.dark.png +0 -0
- package/registry/examples/match-dialog/native/its-a-match.light.png +0 -0
- package/registry/examples/media-capture/native/photo.dark.png +0 -0
- package/registry/examples/media-capture/native/photo.light.png +0 -0
- package/registry/examples/meta-row/native/video.dark.png +0 -0
- package/registry/examples/meta-row/native/video.light.png +0 -0
- package/registry/examples/meter/native/credits.dark.png +0 -0
- package/registry/examples/meter/native/credits.light.png +0 -0
- package/registry/examples/meter/native/split-ring.dark.png +0 -0
- package/registry/examples/meter/native/split-ring.light.png +0 -0
- package/registry/examples/nav-bar/family-product.js +1 -1
- package/registry/examples/nav-bar/family-product.tsx +1 -1
- package/registry/examples/onboarding/native/first-launch.dark.png +0 -0
- package/registry/examples/onboarding/native/first-launch.light.png +0 -0
- package/registry/examples/permission-prompt/native/blocked.dark.png +0 -0
- package/registry/examples/permission-prompt/native/blocked.light.png +0 -0
- package/registry/examples/permission-prompt/native/camera.dark.png +0 -0
- package/registry/examples/permission-prompt/native/camera.light.png +0 -0
- package/registry/examples/photo-grid/native/profile-photos.dark.png +0 -0
- package/registry/examples/photo-grid/native/profile-photos.light.png +0 -0
- package/registry/examples/pricing-page/credits.js +3 -3
- package/registry/examples/pricing-page/credits.tsx +3 -3
- package/registry/examples/product-page/orazaka.js +2 -2
- package/registry/examples/product-page/orazaka.tsx +2 -2
- package/registry/examples/profile-card/native/default.dark.png +0 -0
- package/registry/examples/profile-card/native/default.light.png +0 -0
- package/registry/examples/progress/native/bar.dark.png +0 -0
- package/registry/examples/progress/native/bar.light.png +0 -0
- package/registry/examples/progress/native/indeterminate.dark.png +0 -0
- package/registry/examples/progress/native/indeterminate.light.png +0 -0
- package/registry/examples/progress/native/ring.dark.png +0 -0
- package/registry/examples/progress/native/ring.light.png +0 -0
- package/registry/examples/radio-group/native/buttons.dark.png +0 -0
- package/registry/examples/radio-group/native/buttons.light.png +0 -0
- package/registry/examples/radio-group/native/cards.dark.png +0 -0
- package/registry/examples/radio-group/native/cards.light.png +0 -0
- package/registry/examples/radio-group/native/list.dark.png +0 -0
- package/registry/examples/radio-group/native/list.light.png +0 -0
- package/registry/examples/screen/native/auth.dark.png +0 -0
- package/registry/examples/screen/native/auth.light.png +0 -0
- package/registry/examples/screen/native/chat.dark.png +0 -0
- package/registry/examples/screen/native/chat.light.png +0 -0
- package/registry/examples/screen/native/profile.dark.png +0 -0
- package/registry/examples/screen/native/profile.light.png +0 -0
- package/registry/examples/screen/native/settings.dark.png +0 -0
- package/registry/examples/screen/native/settings.light.png +0 -0
- package/registry/examples/search-field/native/cancel.dark.png +0 -0
- package/registry/examples/search-field/native/cancel.light.png +0 -0
- package/registry/examples/settings-layout/account.js +1 -1
- package/registry/examples/settings-layout/account.tsx +1 -1
- package/registry/examples/settings-layout/native/account.dark.png +0 -0
- package/registry/examples/settings-layout/native/account.light.png +0 -0
- package/registry/examples/settings-layout/sections.js +1 -1
- package/registry/examples/settings-layout/sections.tsx +1 -1
- package/registry/examples/showcase/native/slides.dark.png +0 -0
- package/registry/examples/showcase/native/slides.light.png +0 -0
- package/registry/examples/slider/native/default.dark.png +0 -0
- package/registry/examples/slider/native/default.light.png +0 -0
- package/registry/examples/slider/native/range.dark.png +0 -0
- package/registry/examples/slider/native/range.light.png +0 -0
- package/registry/examples/slider/native/stepped.dark.png +0 -0
- package/registry/examples/slider/native/stepped.light.png +0 -0
- package/registry/examples/slider/native/vertical.dark.png +0 -0
- package/registry/examples/slider/native/vertical.light.png +0 -0
- package/registry/examples/spinner/native/sizes.dark.png +0 -0
- package/registry/examples/spinner/native/sizes.light.png +0 -0
- package/registry/examples/split-view/messages.js +1 -1
- package/registry/examples/split-view/messages.tsx +1 -1
- package/registry/examples/status-page/building.js +1 -1
- package/registry/examples/status-page/building.tsx +1 -1
- package/registry/examples/step-progress/native/sign-up.dark.png +0 -0
- package/registry/examples/step-progress/native/sign-up.light.png +0 -0
- package/registry/examples/stepper/native/run.dark.png +0 -0
- package/registry/examples/stepper/native/run.light.png +0 -0
- package/registry/examples/sticky-action/phone.js +1 -1
- package/registry/examples/sticky-action/phone.tsx +1 -1
- package/registry/examples/switch/native/default.dark.png +0 -0
- package/registry/examples/switch/native/default.light.png +0 -0
- package/registry/examples/switch/native/settings.dark.png +0 -0
- package/registry/examples/switch/native/settings.light.png +0 -0
- package/registry/examples/tab-bar/native/main-sections.dark.png +0 -0
- package/registry/examples/tab-bar/native/main-sections.light.png +0 -0
- package/registry/examples/tabs/native/sections.dark.png +0 -0
- package/registry/examples/tabs/native/sections.light.png +0 -0
- package/registry/examples/tag-selector/native/interests.dark.png +0 -0
- package/registry/examples/tag-selector/native/interests.light.png +0 -0
- package/registry/examples/toast/native/snackbar.dark.png +0 -0
- package/registry/examples/toast/native/snackbar.light.png +0 -0
- package/registry/examples/toast/native/tones.dark.png +0 -0
- package/registry/examples/toast/native/tones.light.png +0 -0
- package/registry/examples/txt/native/tones.dark.png +0 -0
- package/registry/examples/txt/native/tones.light.png +0 -0
- package/registry/examples/update-prompt/native/default.dark.png +0 -0
- package/registry/examples/update-prompt/native/default.light.png +0 -0
- package/registry/examples/verified-badge/native/verified.dark.png +0 -0
- package/registry/examples/verified-badge/native/verified.light.png +0 -0
- package/registry/examples/vertical-video/native/rail.dark.png +0 -0
- package/registry/examples/vertical-video/native/rail.light.png +0 -0
- package/registry/examples/waveform/native/card.dark.png +0 -0
- package/registry/examples/waveform/native/card.light.png +0 -0
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +6 -6
- package/registry/glow-card.json +19 -19
- package/registry/index.json +20 -21
- package/registry/iso.json +7 -7
- package/registry/landing-page.json +2 -2
- package/registry/landscape.json +1 -1
- package/registry/link-card.json +15 -24
- package/registry/map.json +235 -6
- package/registry/marketing-layout.json +3 -3
- package/registry/media-gallery.json +1 -1
- package/registry/nav-bar.json +32 -4
- package/registry/pointer-light.json +17 -22
- package/registry/pricing-page.json +2 -2
- package/registry/product-page.json +2 -2
- package/registry/progress.json +2 -2
- package/registry/radio-group.json +1 -1
- package/registry/rail-nav.json +2 -2
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +3 -3
- package/registry/site-footer.json +2 -2
- package/registry/sites.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/split-view.json +2 -2
- package/registry/status-page.json +1 -1
- package/registry/sticky-action.json +2 -2
- package/registry/update-prompt.json +1 -1
- package/registry/video-player.json +1 -1
- package/dist/chunk-6ZYRUOTA.js.map +0 -1
- package/dist/chunk-AB5D3KBG.js +0 -37
- package/dist/chunk-AB5D3KBG.js.map +0 -1
- package/dist/chunk-APNIVNIX.js.map +0 -1
- package/dist/chunk-CHOJUCVE.js.map +0 -1
- package/dist/chunk-DZU6RMYD.js.map +0 -1
- package/dist/chunk-HAQWKORZ.js.map +0 -1
- package/dist/chunk-KM6VAKIE.js.map +0 -1
- package/dist/chunk-MQPC3P4T.js.map +0 -1
- package/dist/chunk-OUWPBSSL.js.map +0 -1
- package/dist/chunk-TFJDIOFK.js.map +0 -1
- package/registry/examples/argument-card/hover-light.js +0 -17
- package/registry/examples/argument-card/hover-light.tsx +0 -19
- package/registry/examples/link-card/hover-light.d.ts +0 -4
- package/registry/examples/link-card/hover-light.js +0 -18
- package/registry/examples/link-card/hover-light.tsx +0 -20
- /package/dist/{chunk-LYOPPBMC.js.map → chunk-KIBICUZK.js.map} +0 -0
- /package/dist/{chunk-HBZPJ3EP.js.map → chunk-NJFCSVPT.js.map} +0 -0
- /package/registry/examples/{argument-card/hover-light.d.ts → map/route.d.ts} +0 -0
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
"path": "media-gallery/media-gallery.tsx",
|
|
103
|
-
"content": "// Client (the reader's motion preference). A shop's photos and loops in tiles: the first is the featured one, loops play\n// muted while their tile is on screen (a still under reduced motion), a caption rides the foot of a tile.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const mediaGallery = tv({\n slots: {\n root: \"m-0 grid list-none grid-cols-2 gap-2 p-0 sm:gap-3 md:grid-cols-4\",\n tile:\n \"group relative isolate m-0 aspect-[3/4] overflow-hidden rounded-xl border border-border-default bg-media data-[shape=wide]:aspect-[4/3] \" +\n // The featured tile takes two columns; from md up it has no ratio of its own and stretches to the height of the row its neighbours set.\n \"data-[featured]:col-span-2 data-[featured]:aspect-[4/3] md:data-[featured]:aspect-auto\",\n media: \"absolute inset-0 h-full w-full object-cover
|
|
103
|
+
"content": "// Client (the reader's motion preference). A shop's photos and loops in tiles: the first is the featured one, loops play\n// muted while their tile is on screen (a still under reduced motion), a caption rides the foot of a tile.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const mediaGallery = tv({\n slots: {\n root: \"m-0 grid list-none grid-cols-2 gap-2 p-0 sm:gap-3 md:grid-cols-4\",\n tile:\n \"group relative isolate m-0 aspect-[3/4] overflow-hidden rounded-xl border border-border-default bg-media data-[shape=wide]:aspect-[4/3] \" +\n // The featured tile takes two columns; from md up it has no ratio of its own and stretches to the height of the row its neighbours set.\n \"data-[featured]:col-span-2 data-[featured]:aspect-[4/3] md:data-[featured]:aspect-auto\",\n media: \"absolute inset-0 h-full w-full object-cover\",\n caption: \"pointer-events-none absolute inset-x-0 bottom-0 bg-linear-to-t from-scrim-strong to-transparent p-3 pt-8 text-xs font-semibold text-fg-on-media\",\n },\n});\nconst s = mediaGallery();\n\nexport type GalleryMedia = {\n /** A stable id. */\n id: string;\n /** The still: a photo, or the poster of a loop. */\n src: string;\n /** What it shows, in words. */\n alt: string;\n /** A muted loop: WebM first, MP4 for Safari. */\n video?: { webm?: string; mp4?: string };\n /** One line over the foot of the tile. */\n caption?: string;\n /** `wide` for a landscape source (4:3 tile); the default tile is a 3:4 portrait. */\n shape?: \"portrait\" | \"wide\";\n};\n\nexport type MediaGalleryProps = Omit<React.ComponentProps<\"ul\">, \"children\"> & {\n /** The photos and loops, the featured one first. */\n items: readonly GalleryMedia[];\n /** The gallery's accessible name, passed translated. */\n label: string;\n};\n\nfunction Tile({ item, featured }: { item: GalleryMedia; featured: boolean }) {\n const ref = React.useRef<HTMLLIElement>(null);\n const [play, setPlay] = React.useState(false);\n React.useEffect(() => {\n const node = ref.current;\n if (!item.video || !node || window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const io = new IntersectionObserver(([entry]) => setPlay(Boolean(entry?.isIntersecting)), { rootMargin: \"120px\" });\n io.observe(node);\n return () => io.disconnect();\n }, [item.video]);\n return (\n <li ref={ref} className={s.tile()} data-featured={featured ? \"\" : undefined} data-shape={item.shape ?? \"portrait\"}>\n <figure className=\"m-0 h-full w-full\">\n <img src={item.src} alt={item.alt} loading=\"lazy\" decoding=\"async\" className={s.media()} />\n {play && item.video && (\n <video className={s.media()} poster={item.src} autoPlay muted loop playsInline preload=\"none\" aria-hidden tabIndex={-1}>\n {item.video.webm && <source src={item.video.webm} type=\"video/webm\" />}\n {item.video.mp4 && <source src={item.video.mp4} type=\"video/mp4\" />}\n </video>\n )}\n {item.caption && <figcaption className={s.caption()}>{item.caption}</figcaption>}\n </figure>\n </li>\n );\n}\n\n/** Photos and loops in tiles, the first featured; loops play while seen. */\nexport function MediaGallery({ items, label, className, ...props }: MediaGalleryProps) {\n return (\n <ul aria-label={label} className={s.root({ className })} {...props}>\n {items.map((item, i) => (\n <Tile key={item.id} item={item} featured={i === 0 && items.length > 2} />\n ))}\n </ul>\n );\n}\n"
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
"path": "media-gallery/meta.ts",
|
package/registry/nav-bar.json
CHANGED
|
@@ -222,6 +222,13 @@
|
|
|
222
222
|
"default": null,
|
|
223
223
|
"description": "The current page: `aria-current=\"page\"`, and the light rests on it.",
|
|
224
224
|
"required": false
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "itemClassName",
|
|
228
|
+
"type": "string",
|
|
229
|
+
"default": null,
|
|
230
|
+
"description": "Classes on the list item around the link (to show it from a breakpoint only).",
|
|
231
|
+
"required": false
|
|
225
232
|
}
|
|
226
233
|
]
|
|
227
234
|
},
|
|
@@ -257,6 +264,13 @@
|
|
|
257
264
|
"description": "Columns of the panel.",
|
|
258
265
|
"required": false
|
|
259
266
|
},
|
|
267
|
+
{
|
|
268
|
+
"name": "inlineFrom",
|
|
269
|
+
"type": "\"md\" | \"lg\" | \"xl\"",
|
|
270
|
+
"default": "lg",
|
|
271
|
+
"description": "From which breakpoint the inline links stand in the bar. Below it (between the phone panel at `md` and this\nbreakpoint) the section is the dropdown, so long labels never overflow a tablet-width bar. `md` inlines as soon as\nthe bar is shown.",
|
|
272
|
+
"required": false
|
|
273
|
+
},
|
|
260
274
|
{
|
|
261
275
|
"name": "inlineUpTo",
|
|
262
276
|
"type": "number",
|
|
@@ -464,7 +478,7 @@
|
|
|
464
478
|
"description": "A product's site, the standard of every Krizaka product: its mark alone, Product ▾ · Platforms ▾ · Docs with menus (icon · title · a description of two lines), the repository, the theme, the language, Sign in and “Talk to us”; the phone panel groups the same entries and ends with the preferences.",
|
|
465
479
|
"path": "examples/nav-bar/family-product.tsx",
|
|
466
480
|
"module": "@krizaka/ui/registry/examples/nav-bar/family-product",
|
|
467
|
-
"code": "import { OrazakaLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { languagesFor } from \"@krizaka/ui/language-switch\";\nimport { NavBar } from \"@krizaka/ui/nav-bar\";\nimport { ThemeProvider, ThemeToggle } from \"@krizaka/ui/theme\";\n\n// The product standard: mark · Product ▾ · Platforms ▾ · Docs; theme, language, Sign in, one action; phone: groups, preferences.\nconst groups = [\n { id: \"product\", label: \"Product\", links: [[\"#how\", \"How it works\", \"Install to first answer in three commands.\"], [\"#demo\", \"Demo\", \"Real runs: prompts, images, videos.\"]] },\n { id: \"platforms\", label: \"Platforms\", links: [[\"#web\", \"Web\", \"The workspace in your browser.\"], [\"#mac\", \"Mac\", \"Orazaka Desktop for Apple Silicon.\"]] },\n];\nconst icon = <svg data-kz-icon viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden><circle data-node cx=\"12\" cy=\"12\" r=\"4\" /></svg>;\nconst [theme, language] = [<ThemeToggle key=\"t\" label=\"Change the theme\" />, <NavBar.Language key=\"l\" label=\"Language\" current=\"en\" languages={languagesFor(\"/en\")} />];\nexport default function NavBarFamilyProduct() {\n return (\n <ThemeProvider>\n <NavBar.Root label=\"Main\" floating={false}>\n <NavBar.Brand href=\"#home\"><OrazakaLogo size={22} />Orazaka</NavBar.Brand>\n <NavBar.Items>\n {groups.map((g) => (\n <NavBar.Menu key={g.id} value={g.id} label={g.label}>\n {g.links.map(([href, title, line]) => <NavBar.MenuLink key={href} href={href} title={title} description={line} icon={icon} />)}\n </NavBar.Menu>\n ))}\n <NavBar.Link href=\"#docs\">Docs</NavBar.Link></NavBar.Items>\n <NavBar.Actions>\n <span className=\"hidden items-center gap-1 md:inline-flex\">{theme}{language}</span>\n <Button asChild variant=\"ghost\" size=\"sm\" className=\"hidden md:inline-flex\"><a href=\"#sign-in\">Sign in</a></Button>\n <Button asChild variant=\"primary\" size=\"sm\"
|
|
481
|
+
"code": "import { OrazakaLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { languagesFor } from \"@krizaka/ui/language-switch\";\nimport { NavBar } from \"@krizaka/ui/nav-bar\";\nimport { ThemeProvider, ThemeToggle } from \"@krizaka/ui/theme\";\n\n// The product standard: mark · Product ▾ · Platforms ▾ · Docs; theme, language, Sign in, one action; phone: groups, preferences.\nconst groups = [\n { id: \"product\", label: \"Product\", links: [[\"#how\", \"How it works\", \"Install to first answer in three commands.\"], [\"#demo\", \"Demo\", \"Real runs: prompts, images, videos.\"]] },\n { id: \"platforms\", label: \"Platforms\", links: [[\"#web\", \"Web\", \"The workspace in your browser.\"], [\"#mac\", \"Mac\", \"Orazaka Desktop for Apple Silicon.\"]] },\n];\nconst icon = <svg data-kz-icon viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden><circle data-node cx=\"12\" cy=\"12\" r=\"4\" /></svg>;\nconst [theme, language] = [<ThemeToggle key=\"t\" label=\"Change the theme\" />, <NavBar.Language key=\"l\" label=\"Language\" current=\"en\" languages={languagesFor(\"/en\")} />];\nexport default function NavBarFamilyProduct() {\n return (\n <ThemeProvider>\n <NavBar.Root label=\"Main\" floating={false}>\n <NavBar.Brand href=\"#home\"><OrazakaLogo size={22} />Orazaka</NavBar.Brand>\n <NavBar.Items>\n {groups.map((g) => (\n <NavBar.Menu key={g.id} value={g.id} label={g.label}>\n {g.links.map(([href, title, line]) => <NavBar.MenuLink key={href} href={href} title={title} description={line} icon={icon} />)}\n </NavBar.Menu>\n ))}\n <NavBar.Link href=\"#docs\">Docs</NavBar.Link></NavBar.Items>\n <NavBar.Actions>\n <span className=\"hidden items-center gap-1 md:inline-flex\">{theme}{language}</span>\n <Button asChild variant=\"ghost\" size=\"sm\" className=\"hidden md:inline-flex\"><a href=\"#sign-in\">Sign in</a></Button>\n <Button asChild variant=\"primary\" size=\"sm\" className=\"hidden md:inline-flex\"><a href=\"#talk\">Talk to us</a></Button>\n <NavBar.Mobile openLabel=\"Open the menu\" closeLabel=\"Close the menu\">\n {groups.map((g) => (\n <NavBar.MobileGroup key={g.id} label={g.label}>{g.links.map(([href, title]) => <NavBar.MobileLink key={href} href={href}>{title}</NavBar.MobileLink>)}</NavBar.MobileGroup>\n ))}\n <NavBar.MobileLink href=\"#docs\">Docs</NavBar.MobileLink><NavBar.MobilePreferences label=\"Preferences\">{theme}{language}</NavBar.MobilePreferences>\n </NavBar.Mobile>\n </NavBar.Actions>\n </NavBar.Root>\n </ThemeProvider>\n );\n}\n"
|
|
468
482
|
},
|
|
469
483
|
{
|
|
470
484
|
"name": "family-admin",
|
|
@@ -552,7 +566,7 @@
|
|
|
552
566
|
},
|
|
553
567
|
{
|
|
554
568
|
"path": "marks/krizaka-geometry.ts",
|
|
555
|
-
"content": "import { marks } from \"@krizaka/tokens/native\";\n\n/**\n * The Krizaka mark's geometry — the one description drawn by the web mark (KrizakaLogo) and the native one\n * (KrizakaMark). Built on the Orochia mark's grammar (docs: packages/tokens/BRAND.md, \"Marks\"): a 360×360 artwork at\n * (20, 20) in a 400×400 view box, one bold stroke (22) drawn as an open figure, an end that breaks out as a node, a\n * core, a dashed orbit and a ring; below 48 px the view box crops to the emblem.\n *\n * Krizaka is the parent: the hexagonal shield is **ink** (the host's text roles), the node and the core carry the\n * Krizaka blue (`marks.krizaka` of @krizaka/tokens: the brand's accent hsl(
|
|
569
|
+
"content": "import { marks } from \"@krizaka/tokens/native\";\n\n/**\n * The Krizaka mark's geometry — the one description drawn by the web mark (KrizakaLogo) and the native one\n * (KrizakaMark). Built on the Orochia mark's grammar (docs: packages/tokens/BRAND.md, \"Marks\"): a 360×360 artwork at\n * (20, 20) in a 400×400 view box, one bold stroke (22) drawn as an open figure, an end that breaks out as a node, a\n * core, a dashed orbit and a ring; below 48 px the view box crops to the emblem.\n *\n * Krizaka is the parent: the hexagonal shield is **ink** (the host's text roles), the node and the core carry the\n * Krizaka blue (`marks.krizaka` of @krizaka/tokens: the brand's accent hsl(208 46% 45%) → its text tint hsl(206 58% 72%)).\n */\nexport const KRIZAKA = {\n viewBox: \"0 0 400 400\",\n croppedViewBox: \"86 82 228 228\",\n offset: 20,\n /** The shield: a pointy-top hexagon (r 92 around 180,180), open at its upper-right edge. */\n shield: \"M259.7,134 L259.7,226 L180,272 L100.3,226 L100.3,134 L180,88 L212,106.5\",\n stroke: 22,\n ink: { x1: 70, y1: 290, x2: 290, y2: 70 },\n blue: { x1: 180, y1: 180, x2: 300, y2: 60, stops: marks.krizaka },\n /** The vertex where the shield opens: a node (the serpent's head in Orochia's grammar). */\n node: { cx: 259.7, cy: 134, r: 19, inner: 6 },\n core: { cx: 180, cy: 180, r: 24, inner: 8 },\n highlight: \"#ddeaf4\",\n glow: { color: marks.krizaka[0]![1], opacity: 0.16 },\n orbit: { r: 160, dash: \"8 8\", width: 1.5 },\n ring: { r: 134, width: 1 },\n} as const;\n"
|
|
556
570
|
},
|
|
557
571
|
{
|
|
558
572
|
"path": "marks/orazaka-geometry.ts",
|
|
@@ -592,7 +606,7 @@
|
|
|
592
606
|
},
|
|
593
607
|
{
|
|
594
608
|
"path": "nav-bar/nav-bar.tsx",
|
|
595
|
-
"content": "// Client (scroll, a sliding indicator, menus). Radix NavigationMenu does the menus' semantics and keyboard (Enter, Space\n// and ↓ open; ↑ ↓ Home End walk; Escape closes and returns the focus; Tab out closes); this file draws the Krizaka bar:\n// a floating pill that tightens once the page scrolls, one light that glides to the item under the pointer or the\n// focus and rests on the current page, and a mobile panel that is a plain disclosure (no focus trap, Escape closes).\nimport { NavigationMenu as Nav, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { FamilyMark, GitHubGlyph } from \"../family-mark\";\nimport { LanguageSwitch, type LanguageSwitchProps } from \"../language-switch/language-switch\";\nimport { type FamilyLabels, familyLabels, type Site, type SiteId, sitesByGroup } from \"../sites/sites\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { placePanel } from \"./placement\";\n\nexport const navBar = tv({\n slots: {\n root:\n \"group/nav relative z-40 mx-auto flex h-14 w-full items-center gap-2 border-border-subtle bg-surface-1/75 backdrop-blur-xl backdrop-saturate-150 \" +\n \"transition-[height,background-color,border-color,box-shadow] duration-300 motion-reduce:transition-none \" +\n \"data-compact:h-12 data-compact:border-border-default data-compact:bg-surface-1/90 data-compact:shadow-lg\",\n brand:\n \"mr-2 inline-flex shrink-0 items-center gap-2 font-display text-[15px] font-bold tracking-tight text-fg no-underline \" +\n \"rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n items: \"relative hidden md:block\",\n list:\n \"group/list relative isolate flex items-center gap-0.5 \" +\n // The light: under the item that is pointed at or focused, resting on the current page.\n \"before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-full before:bg-surface-3 before:opacity-0 before:transition-[translate,width,opacity] before:duration-300 motion-reduce:before:transition-none \" +\n \"data-[indicator=on]:before:opacity-100\",\n item:\n \"inline-flex h-9 select-none items-center gap-1 rounded-full px-3.5 text-sm font-medium text-fg-secondary no-underline transition-colors \" +\n \"hover:text-fg data-active:text-fg data-[state=open]:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n // Before the light is ready (server render), the current page carries its own tint.\n \"group-not-data-[indicator=on]/list:data-active:bg-surface-3\",\n chevron: \"h-3.5 w-3.5 transition-transform duration-300 group-data-[state=open]/trigger:rotate-180 motion-reduce:transition-none\",\n viewport:\n // `left` is set by NavBarItems (--kz-menu-left): the panel's edge under its trigger, kept inside the page.\n \"kz-pop absolute left-[var(--kz-menu-left,0px)] top-full mt-2 h-(--radix-navigation-menu-viewport-height) w-(--radix-navigation-menu-viewport-width) origin-top-left overflow-hidden \" +\n \"rounded-2xl border border-border-default bg-surface-1 shadow-lg transition-[left,width,height] duration-300 motion-reduce:transition-none\",\n // Icon · title · a description of two lines at most. 28 rem (40 rem in two columns), never wider than the page\n // between its gutters, and never taller than the screen.\n panel: \"grid w-[min(28rem,calc(100vw-2rem))] max-h-[calc(100dvh-6rem)] gap-0.5 overflow-y-auto p-1.5\",\n panelWide: \"w-[min(40rem,calc(100vw-2rem))] md:grid-cols-2\",\n menuLink:\n \"group/link flex items-start gap-3 rounded-lg px-2.5 py-2 text-left no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring data-active:bg-accent-soft\",\n menuIcon:\n \"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border-default bg-surface-2 text-fg-accent \" +\n \"transition-[transform,border-color] duration-300 group-hover/link:-translate-y-0.5 group-hover/link:border-accent [&>svg]:h-4 [&>svg]:w-4 motion-reduce:transform-none\",\n menuTitle: \"block line-clamp-2 wrap-break-word text-sm font-semibold leading-5 text-fg\",\n // Two lines at most, wrapped at word boundaries — a longer one ends in an ellipsis, never cut mid-sentence.\n menuText: \"mt-0.5 block line-clamp-2 wrap-break-word text-xs leading-4 text-fg-secondary\",\n actions: \"ml-auto flex items-center gap-2\",\n toggle:\n \"inline-flex h-9 w-9 items-center justify-center rounded-full border border-border-subtle bg-surface-2/60 text-fg md:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n mobile: \"kz-pop absolute inset-x-0 top-full mt-2 grid gap-1 rounded-2xl border border-border-default bg-surface-1 p-2 shadow-lg md:hidden\",\n mobileGroup: \"grid gap-0.5 [&+&]:mt-1 [&+&]:border-t [&+&]:border-border-subtle [&+&]:pt-2\",\n mobileGroupTitle: \"px-3 pb-1 pt-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n mobileLink:\n \"flex items-center justify-between rounded-xl px-3 py-3 text-[15px] font-medium text-fg no-underline hover:bg-surface-2 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:text-fg-accent\",\n // The family switcher: a chevron attached to the brand, its panel aligned on the bar's left edge.\n family: \"hidden md:block\",\n familyTrigger:\n \"group/trigger -ml-2 mr-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-fg-secondary transition-colors \" +\n \"hover:bg-surface-3 hover:text-fg data-[state=open]:bg-surface-3 data-[state=open]:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n familyViewport:\n \"kz-pop absolute left-0 top-full mt-2 h-(--radix-navigation-menu-viewport-height) w-(--radix-navigation-menu-viewport-width) origin-top-left overflow-hidden \" +\n \"rounded-2xl border border-border-default bg-surface-1 shadow-lg transition-[width,height] duration-300 motion-reduce:transition-none\",\n familyPanel: \"grid w-[min(92vw,42rem)] gap-x-2 gap-y-3 p-3 sm:grid-cols-2\",\n familyGroupTitle: \"px-3 pb-1 pt-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n familyLink:\n \"group/link flex items-start gap-3 rounded-xl px-3 py-2.5 text-left no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring data-current:bg-accent-soft\",\n familyMark: \"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center text-fg\",\n familyName: \"flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm font-semibold text-fg\",\n familyText: \"mt-0.5 block text-xs leading-relaxed text-fg-secondary\",\n familyTag: \"rounded-full px-1.5 py-px text-[10px] font-semibold uppercase tracking-wider\",\n mobileFamily: \"mt-1 grid gap-2 border-t border-border-subtle px-1 pb-1 pt-3\",\n mobileFamilyLink:\n \"flex min-h-11 items-center gap-3 rounded-xl px-2 py-2 text-[15px] font-medium text-fg no-underline hover:bg-surface-2 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-current:bg-accent-soft\",\n badge:\n \"hidden h-6 shrink-0 items-center gap-1 rounded-full border border-border-subtle bg-surface-2 px-2 font-mono text-[11px] font-medium text-fg-secondary sm:inline-flex\",\n repo:\n \"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n floating: {\n true: { root: \"max-w-5xl rounded-full border pl-5 pr-2 data-compact:max-w-4xl\" },\n false: { root: \"border-b px-4 md:px-6\" },\n },\n },\n defaultVariants: { floating: true },\n});\nconst s = navBar();\n\ninterface NavBarContext {\n label: string;\n open: boolean;\n setOpen: (open: boolean) => void;\n panelId: string;\n}\nconst Context = React.createContext<NavBarContext | null>(null);\n/** The open menu of `NavBar.Items` (`\"\"`: none), so a plain link can close it when the pointer moves on to it. */\nconst ItemsContext = React.createContext<{ close: () => void }>({ close: () => {} });\n/** True inside `NavBar.Mobile`: the family renders as the panel's last group instead of a switcher. */\nconst MobileContext = React.createContext(false);\nconst useNavBar = () => {\n const ctx = React.useContext(Context);\n if (!ctx) throw new Error(\"NavBar parts must be inside <NavBar.Root>.\");\n return ctx;\n};\n\nexport type NavBarRootProps = React.ComponentProps<\"header\"> & {\n /** The accessible name of the navigation (“Main”) — passed translated. */\n label: string;\n /**\n * Pixels of scroll after which the bar tightens (denser, lower, lifted). `false`: never.\n * @default 24\n */\n compactAfter?: number | false;\n /**\n * A floating pill (the Krizaka bar) or a full-width bar with a hairline under it.\n * @default true\n */\n floating?: boolean;\n};\n\n/** The bar. Place it in a `sticky top-3` (floating) or `sticky top-0` wrapper — or let it scroll. */\nexport function NavBarRoot({ label, compactAfter = 24, floating = true, className, children, ...props }: NavBarRootProps) {\n const [compact, setCompact] = React.useState(false);\n const [open, setOpen] = React.useState(false);\n const panelId = React.useId();\n React.useEffect(() => {\n if (compactAfter === false) return;\n const onScroll = () => setCompact(window.scrollY > compactAfter);\n onScroll();\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => window.removeEventListener(\"scroll\", onScroll);\n }, [compactAfter]);\n const ctx = React.useMemo(() => ({ label, open, setOpen, panelId }), [label, open, panelId]);\n return (\n <Context.Provider value={ctx}>\n <header data-compact={compact ? \"\" : undefined} data-floating={floating} className={navBar({ floating }).root({ className })} {...props}>\n {children}\n </header>\n </Context.Provider>\n );\n}\n\nexport type NavBarBrandProps = React.ComponentProps<\"a\"> & {\n /** Renders your link (a router `<Link>`) with the brand's look. */\n asChild?: boolean;\n};\n\n/** The brand: a mark and a name, linking home. */\nexport function NavBarBrand({ asChild, className, ...props }: NavBarBrandProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp className={s.brand({ className })} {...props} />;\n}\n\nexport type NavBarItemsProps = Omit<React.ComponentProps<typeof Nav.Root>, \"orientation\">;\n\n/**\n * The desktop navigation: links and menus, with the light that glides between them. Hidden below `md` (see\n * `NavBar.Mobile`). A menu opens on click, Enter, Space or ↓ — or when the pointer rests on its trigger (hover intent,\n * `delayDuration`, 200 ms): passing over the bar opens nothing. While a menu is open the light stays on its trigger;\n * moving on to a plain link closes the menu, so the bar never shows two current items. A click outside or Escape closes.\n */\nexport function NavBarItems({ className, children, value: valueProp, defaultValue, onValueChange, delayDuration = 200, ...props }: NavBarItemsProps) {\n const { label } = useNavBar();\n const [inner, setInner] = React.useState(defaultValue ?? \"\");\n const value = valueProp ?? inner;\n const setValue = React.useCallback(\n (next: string) => {\n setInner(next);\n onValueChange?.(next);\n },\n [onValueChange],\n );\n const items = React.useMemo(() => ({ close: () => setValue(\"\") }), [setValue]);\n const { ref, handlers } = useSlidingIndicator<HTMLUListElement>({\n item: \"[data-nav-item]\",\n active: \"[data-nav-item][data-active]\",\n prefer: \"[data-nav-item][data-state=open]\",\n follow: true,\n });\n const root = React.useRef<HTMLElement>(null);\n const viewport = React.useRef<HTMLDivElement>(null);\n // The open panel sits under its trigger and stays inside the page (collision): measured when a menu opens, when\n // the panel changes size (another menu) and when the window does.\n React.useLayoutEffect(() => {\n const panel = viewport.current;\n const nav = root.current;\n if (!panel || !nav || !value) return;\n const place = () => {\n const trigger = nav.querySelector<HTMLElement>(\"[data-nav-item][data-state=open]\");\n const width = Number.parseFloat(panel.style.getPropertyValue(\"--radix-navigation-menu-viewport-width\"));\n if (!trigger || !Number.isFinite(width)) return;\n const left = `${placePanel({ trigger: trigger.getBoundingClientRect().left, root: nav.getBoundingClientRect().left, panel: width, window: document.documentElement.clientWidth })}px`;\n if (panel.style.getPropertyValue(\"--kz-menu-left\") !== left) panel.style.setProperty(\"--kz-menu-left\", left);\n };\n place();\n const watch = new MutationObserver(place);\n watch.observe(panel, { attributes: true, attributeFilter: [\"style\"] });\n window.addEventListener(\"resize\", place);\n return () => {\n watch.disconnect();\n window.removeEventListener(\"resize\", place);\n };\n }, [value]);\n return (\n <ItemsContext.Provider value={items}>\n <Nav.Root ref={root} aria-label={label} className={s.items({ className })} value={value} onValueChange={setValue} delayDuration={delayDuration} {...props}>\n <Nav.List ref={ref} className={s.list()} {...handlers}>\n {children}\n </Nav.List>\n <Nav.Viewport ref={viewport} className={s.viewport()} />\n </Nav.Root>\n </ItemsContext.Provider>\n );\n}\n\nexport type NavBarLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"active\"> & {\n /** The current page: `aria-current=\"page\"`, and the light rests on it. */\n active?: boolean;\n};\n\n/** A link of the bar. `asChild` renders your router's `<Link>`. */\nexport function NavBarLink({ active, className, onPointerEnter, ...props }: NavBarLinkProps) {\n const { close } = React.useContext(ItemsContext);\n return (\n <Nav.Item>\n <Nav.Link\n data-nav-item=\"\"\n data-active={active ? \"\" : undefined}\n active={active}\n aria-current={active ? \"page\" : undefined}\n className={s.item({ className })}\n onPointerEnter={(e) => {\n onPointerEnter?.(e);\n close();\n }}\n {...props}\n />\n </Nav.Item>\n );\n}\n\nexport type NavBarMenuProps = Omit<React.ComponentProps<typeof Nav.Item>, \"children\"> & {\n /** The trigger's words (“Products”) — passed translated. */\n label: string;\n /** The current page is inside this menu: the light rests on its trigger. */\n active?: boolean;\n /** The menu's links (`NavBar.MenuLink`), laid out in a panel; `columns` sets the grid. */\n children: React.ReactNode;\n /**\n * Columns of the panel.\n * @default 1\n */\n columns?: 1 | 2;\n /**\n * A section with this many links or fewer needs no hidden list: they sit in the bar as plain links (the active one\n * marked, no dropdown, `label` unused on the desktop). Above it, a dropdown. `0` always makes a dropdown.\n * @default 3\n */\n inlineUpTo?: number;\n};\n\n/**\n * A menu of the bar: its trigger, and its links in a panel that opens under the bar. A menu of `inlineUpTo` links or\n * fewer (3 by default) is not a menu at all: its links stand in the bar, each with its active state — a hidden list of\n * three is one click more for nothing. The phone's panel groups them as usual (`NavBar.MobileGroup`).\n */\nexport function NavBarMenu({ label, active, columns = 1, inlineUpTo = 3, children, className, ...props }: NavBarMenuProps) {\n const links = React.Children.toArray(children);\n if (links.length > 0 && links.length <= inlineUpTo && links.every((c) => React.isValidElement(c) && c.type === NavBarMenuLink)) {\n return (\n <>\n {links.map((link) => {\n const { title, href, active: here, asChild, children: child, icon: _icon, description: _description, className: linkClass, ...rest } = (link as React.ReactElement<NavBarMenuLinkProps>).props;\n return (\n <NavBarLink key={(link as React.ReactElement).key} asChild={asChild} href={asChild ? undefined : href} active={here} className={linkClass} {...rest}>\n {asChild && React.isValidElement<{ children?: React.ReactNode }>(child) ? React.cloneElement(child, undefined, title) : title}\n </NavBarLink>\n );\n })}\n </>\n );\n }\n return (\n <Nav.Item className={className} {...props}>\n <Nav.Trigger data-nav-item=\"\" data-active={active ? \"\" : undefined} className={cn(s.item(), \"group/trigger\")}>\n {label}\n <svg viewBox=\"0 0 24 24\" className={s.chevron()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </Nav.Trigger>\n <Nav.Content className={cn(s.panel(), columns === 2 && s.panelWide())}>{children}</Nav.Content>\n </Nav.Item>\n );\n}\n\nexport type NavBarMenuLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"title\" | \"active\"> & {\n /** The link's name — passed translated. */\n title: string;\n /** One line under it — passed translated. */\n description?: string;\n /** An icon (a signature icon of `@krizaka/icons`: its node lights up on hover). */\n icon?: React.ReactNode;\n /** The current page. */\n active?: boolean;\n};\n\n/** A link inside a menu: an icon, a name and a line of text. */\nexport function NavBarMenuLink({ title, description, icon, active, href, className, asChild, children, ...props }: NavBarMenuLinkProps) {\n const body = (\n <>\n {icon && <span className={s.menuIcon()}>{icon}</span>}\n <span className=\"min-w-0 flex-1\">\n <span className={s.menuTitle()}>{title}</span>\n {description && <span className={s.menuText()}>{description}</span>}\n </span>\n </>\n );\n return (\n <Nav.Link asChild={asChild} href={asChild ? undefined : href} active={active} aria-current={active ? \"page\" : undefined} data-active={active ? \"\" : undefined} className={s.menuLink({ className })} {...props}>\n {asChild && React.isValidElement<{ children?: React.ReactNode }>(children) ? React.cloneElement(children, undefined, body) : body}\n </Nav.Link>\n );\n}\n\n/** The right side: search, theme, language, the call to action. */\nexport function NavBarActions({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.actions({ className })} {...props} />;\n}\n\nexport type NavBarMobileProps = React.ComponentProps<\"nav\"> & {\n /** The toggle's name when closed (“Open the menu”) — passed translated. */\n openLabel: string;\n /** The toggle's name when open (“Close the menu”) — passed translated. */\n closeLabel: string;\n};\n\n/**\n * Below `md`: a toggle, and a panel under the bar with its links (`NavBar.MobileLink`), grouped like the bar's menus\n * (`NavBar.MobileGroup`). A disclosure: Escape, a click on a link or a press outside close it; Escape returns the focus\n * to the toggle.\n */\nexport function NavBarMobile({ openLabel, closeLabel, className, children, ...props }: NavBarMobileProps) {\n const { label, open, setOpen, panelId } = useNavBar();\n const toggle = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n setOpen(false);\n toggle.current?.focus();\n };\n // A press outside the panel and its toggle closes it (the page under it stays usable).\n const onPress = (e: PointerEvent) => {\n const target = e.target as Node | null;\n if (!target || toggle.current?.contains(target) || document.getElementById(panelId)?.contains(target)) return;\n setOpen(false);\n };\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(\"pointerdown\", onPress);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(\"pointerdown\", onPress);\n };\n }, [open, setOpen, panelId]);\n return (\n <>\n <button ref={toggle} type=\"button\" aria-expanded={open} aria-controls={panelId} aria-label={open ? closeLabel : openLabel} onClick={() => setOpen(!open)} className={s.toggle()}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4.5 w-4.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n {open ? <path d=\"M18 6 6 18M6 6l12 12\" /> : <path d=\"M4 7h16M4 12h16M4 17h10\" />}\n </svg>\n </button>\n {open && (\n <nav\n id={panelId}\n aria-label={label}\n className={s.mobile({ className })}\n onClick={(e) => {\n if ((e.target as Element).closest(\"a\")) setOpen(false);\n }}\n {...props}\n >\n <MobileContext.Provider value>{children}</MobileContext.Provider>\n </nav>\n )}\n </>\n );\n}\n\nexport type NavBarMobileGroupProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The group's heading — the same words as the bar's menu (“Product”) — passed translated. */\n label: string;\n};\n\n/** A group of the mobile panel: the links of one of the bar's menus under its name, in the same order. */\nexport function NavBarMobileGroup({ label, className, children, ...props }: NavBarMobileGroupProps) {\n const id = React.useId();\n return (\n <div role=\"group\" aria-labelledby={id} className={s.mobileGroup({ className })} {...props}>\n <p id={id} className={s.mobileGroupTitle()}>\n {label}\n </p>\n {children}\n </div>\n );\n}\n\nexport type NavBarMobileLinkProps = React.ComponentProps<\"a\"> & {\n /** The current page. */\n active?: boolean;\n /** Renders your router's `<Link>`. */\n asChild?: boolean;\n};\n\n/** A link of the mobile panel. */\nexport function NavBarMobileLink({ active, asChild, className, ...props }: NavBarMobileLinkProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp aria-current={active ? \"page\" : undefined} className={s.mobileLink({ className })} {...props} />;\n}\n\n\nconst tagTone = {\n upcoming: \"bg-accent-soft text-fg ring-1 ring-inset ring-accent/40\",\n construction: \"bg-warning/15 text-fg ring-1 ring-inset ring-warning/40\",\n development: \"bg-surface-3 text-fg ring-1 ring-inset ring-border-strong\",\n current: \"bg-surface-3 text-fg-secondary\",\n};\n\n/** A site's name and its tags: status when not released, “You are here” on the current one. */\nfunction FamilyName({ site, current, labels }: { site: Site; current: boolean; labels: FamilyLabels }) {\n return (\n <span className={s.familyName()}>\n {site.name}\n {site.status !== \"released\" && <span className={cn(s.familyTag(), tagTone[site.status])}>{labels.status[site.status]}</span>}\n {current && <span className={cn(s.familyTag(), tagTone.current)}>{labels.current}</span>}\n </span>\n );\n}\n\nexport type NavBarFamilyProps = {\n /** The site this bar belongs to (`@krizaka/ui/sites`): marked in the family, `aria-current=\"true\"`. */\n current: SiteId;\n /**\n * The family's words, passed translated: `familyLabels.fr` from `@krizaka/ui/sites`, or your own of the same shape.\n * @default familyLabels.en\n */\n labels?: FamilyLabels;\n /** Opens the panel at first render (a story, a screenshot). */\n defaultOpen?: boolean;\n /** Classes of the switcher (or, inside `NavBar.Mobile`, of the family's group). */\n className?: string;\n};\n\n/**\n * The family switcher, attached to the brand (place it right after `NavBar.Brand`): a chevron that opens every site of\n * Krizaka grouped — Krizaka · Products · Open source · GitHub — the current one marked. It opens on click, Enter, Space\n * or ↓ (never on hover); ↑ ↓ walk the sites, Escape closes and returns the focus. Hidden below `md`: place a second\n * `NavBar.Family` as the last child of `NavBar.Mobile`, where it renders as the panel's last group.\n */\nexport function NavBarFamily({ current, labels = familyLabels.en, defaultOpen, className }: NavBarFamilyProps) {\n const inMobile = React.useContext(MobileContext);\n const id = React.useId();\n const groups = sitesByGroup();\n if (inMobile) {\n return (\n <div role=\"group\" aria-labelledby={`${id}-family`} className={s.mobileFamily({ className })}>\n <p id={`${id}-family`} className={s.familyGroupTitle()}>\n {labels.family}\n </p>\n {groups.map(({ group, sites }) => (\n <div key={group}>\n {/* The company's own group is named like the family itself: its heading would repeat the panel's. */}\n {group !== \"krizaka\" && (\n <p id={`${id}-${group}`} className={s.familyGroupTitle()}>\n {labels.groups[group]}\n </p>\n )}\n <ul aria-labelledby={group !== \"krizaka\" ? `${id}-${group}` : `${id}-family`} className=\"grid gap-0.5\">\n {sites.map((site) => (\n <li key={site.id}>\n <a href={site.url} data-current={site.id === current ? \"\" : undefined} aria-current={site.id === current ? \"true\" : undefined} className={s.mobileFamilyLink()}>\n <span className={s.familyMark()}>\n <FamilyMark mark={site.mark} size={24} />\n </span>\n <FamilyName site={site} current={site.id === current} labels={labels} />\n </a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n );\n }\n // Click only: a pointer passing over the brand never opens the family (Radix opens its menus on hover by default).\n const noHover = (e: React.PointerEvent) => e.preventDefault();\n const columns = [groups.filter((g) => g.group === \"krizaka\" || g.group === \"products\"), groups.filter((g) => g.group === \"open-source\" || g.group === \"github\")];\n return (\n <Nav.Root aria-label={labels.family} defaultValue={defaultOpen ? \"family\" : undefined} className={s.family({ className })}>\n <Nav.List className=\"flex\">\n <Nav.Item value=\"family\">\n <Nav.Trigger aria-label={labels.family} onPointerMove={noHover} onPointerLeave={noHover} className={s.familyTrigger()}>\n <svg viewBox=\"0 0 24 24\" className={s.chevron()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </Nav.Trigger>\n <Nav.Content onPointerLeave={noHover} className={s.familyPanel()}>\n {columns.map((column, i) => (\n <div key={i} className=\"grid content-start gap-3\">\n {column.map(({ group, sites }) => (\n <div key={group}>\n <p id={`${id}-${group}`} className={s.familyGroupTitle()}>\n {labels.groups[group]}\n </p>\n <ul aria-labelledby={`${id}-${group}`} className=\"grid gap-0.5\">\n {sites.map((site) => (\n <li key={site.id}>\n <Nav.Link href={site.url} data-current={site.id === current ? \"\" : undefined} aria-current={site.id === current ? \"true\" : undefined} className={s.familyLink()}>\n <span className={s.familyMark()}>\n <FamilyMark mark={site.mark} size={28} />\n </span>\n <span className=\"min-w-0\">\n <FamilyName site={site} current={site.id === current} labels={labels} />\n <span className={s.familyText()}>{labels.descriptions[site.id]}</span>\n </span>\n </Nav.Link>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n ))}\n </Nav.Content>\n </Nav.Item>\n </Nav.List>\n <Nav.Viewport onPointerLeave={noHover} className={s.familyViewport()} />\n </Nav.Root>\n );\n}\n\nexport type NavBarBadgeProps = React.ComponentProps<\"span\"> & {\n /** The environment, when it is not production (`DEV`). */\n env?: string;\n /** The version the site runs (`1.2.3`, shown `v1.2.3`). */\n version?: string;\n};\n\n/** A small mono badge beside the brand: the environment and the version (`DEV · v1.2.3`). Hidden below `sm`. */\nexport function NavBarBadge({ env, version, className, children, ...props }: NavBarBadgeProps) {\n const parts = [env, version && `v${version.replace(/^v/, \"\")}`].filter(Boolean);\n return (\n <span className={s.badge({ className })} {...props}>\n {parts.join(\" · \")}\n {children}\n </span>\n );\n}\n\nexport type NavBarLanguageProps = Omit<LanguageSwitchProps, \"variant\">;\n\n/**\n * The language switch, the same on every site: in the bar a globe and the current code (FR) opening a short menu of\n * the languages by their own names; inside `NavBar.Mobile` the same languages as a row in the preferences.\n */\nexport function NavBarLanguage(props: NavBarLanguageProps) {\n const inMobile = React.useContext(MobileContext);\n return <LanguageSwitch variant={inMobile ? \"inline\" : \"menu\"} {...props} />;\n}\n\nexport type NavBarMobilePreferencesProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The group's heading (“Preferences”) — passed translated. */\n label: string;\n};\n\n/** The last group of the mobile panel: the preferences (theme, `NavBar.Language`), in a row under their heading. */\nexport function NavBarMobilePreferences({ label, className, children, ...props }: NavBarMobilePreferencesProps) {\n const id = React.useId();\n return (\n <div role=\"group\" aria-labelledby={id} className={s.mobileGroup({ className })} {...props}>\n <p id={id} className={s.mobileGroupTitle()}>\n {label}\n </p>\n <div className=\"flex flex-wrap items-center gap-2 px-3 py-1\">{children}</div>\n </div>\n );\n}\n\nexport type NavBarRepoProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The repository the site is built from. */\n href: string;\n /** The link's accessible name (“Source on GitHub”) — passed translated. */\n label: string;\n};\n\n/** The site's repository on GitHub, an icon link of `NavBar.Actions`. */\nexport function NavBarRepo({ label, className, ...props }: NavBarRepoProps) {\n return (\n <a aria-label={label} className={s.repo({ className })} {...props}>\n <GitHubGlyph size={18} />\n </a>\n );\n}\n\n/**\n * `NavBar.Root/Brand/Family/Badge/Items/Link/Menu/MenuLink/Actions/Repo/Language/Mobile/MobileGroup/MobilePreferences/MobileLink` — the Krizaka navigation bar,\n * the same on every site of the family: floating, tightening on scroll, the family switcher on the brand, one light\n * gliding between its items, accessible menus, a mobile panel.\n */\nexport const NavBar = {\n Root: NavBarRoot,\n Brand: NavBarBrand,\n Family: NavBarFamily,\n Badge: NavBarBadge,\n Items: NavBarItems,\n Link: NavBarLink,\n Menu: NavBarMenu,\n MenuLink: NavBarMenuLink,\n Actions: NavBarActions,\n Repo: NavBarRepo,\n Language: NavBarLanguage,\n Mobile: NavBarMobile,\n MobilePreferences: NavBarMobilePreferences,\n MobileGroup: NavBarMobileGroup,\n MobileLink: NavBarMobileLink,\n};\n"
|
|
609
|
+
"content": "// Client (scroll, a sliding indicator, menus). Radix NavigationMenu does the menus' semantics and keyboard (Enter, Space\n// and ↓ open; ↑ ↓ Home End walk; Escape closes and returns the focus; Tab out closes); this file draws the Krizaka bar:\n// a floating pill that tightens once the page scrolls, one light that glides to the item under the pointer or the\n// focus and rests on the current page, and a mobile panel that is a plain disclosure (no focus trap, Escape closes).\nimport { NavigationMenu as Nav, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { FamilyMark, GitHubGlyph } from \"../family-mark\";\nimport { LanguageSwitch, type LanguageSwitchProps } from \"../language-switch/language-switch\";\nimport { type FamilyLabels, familyLabels, type Site, type SiteId, sitesByGroup } from \"../sites/sites\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { placePanel } from \"./placement\";\n\nexport const navBar = tv({\n slots: {\n root:\n \"group/nav relative z-40 mx-auto flex h-14 w-full items-center gap-2 border-border-subtle bg-surface-1/75 backdrop-blur-xl backdrop-saturate-150 \" +\n \"transition-[height,background-color,border-color,box-shadow] duration-300 motion-reduce:transition-none \" +\n \"data-compact:h-12 data-compact:border-border-default data-compact:bg-surface-1/90 data-compact:shadow-lg\",\n brand:\n \"mr-2 inline-flex shrink-0 items-center gap-2 font-display text-[15px] font-bold tracking-tight text-fg no-underline \" +\n \"rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n items: \"relative hidden md:block\",\n list:\n \"group/list relative isolate flex items-center gap-0.5 \" +\n // The light: under the item that is pointed at or focused, resting on the current page.\n \"before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-full before:bg-surface-3 before:opacity-0 before:transition-[translate,width,opacity] before:duration-300 motion-reduce:before:transition-none \" +\n \"data-[indicator=on]:before:opacity-100\",\n item:\n \"inline-flex h-9 select-none items-center gap-1 rounded-full px-3.5 text-sm font-medium text-fg-secondary no-underline transition-colors \" +\n \"hover:text-fg data-active:text-fg data-[state=open]:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n // Before the light is ready (server render), the current page carries its own tint.\n \"group-not-data-[indicator=on]/list:data-active:bg-surface-3\",\n chevron: \"h-3.5 w-3.5 transition-transform duration-300 group-data-[state=open]/trigger:rotate-180 motion-reduce:transition-none\",\n viewport:\n // `left` is set by NavBarItems (--kz-menu-left): the panel's edge under its trigger, kept inside the page.\n \"kz-pop absolute left-[var(--kz-menu-left,0px)] top-full mt-2 h-(--radix-navigation-menu-viewport-height) w-(--radix-navigation-menu-viewport-width) origin-top-left overflow-hidden \" +\n \"rounded-2xl border border-border-default bg-surface-1 shadow-lg transition-[left,width,height] duration-300 motion-reduce:transition-none\",\n // Icon · title · a description of two lines at most. 28 rem (40 rem in two columns), never wider than the page\n // between its gutters, and never taller than the screen.\n panel: \"grid w-[min(28rem,calc(100vw-2rem))] max-h-[calc(100dvh-6rem)] gap-0.5 overflow-y-auto p-1.5\",\n panelWide: \"w-[min(40rem,calc(100vw-2rem))] md:grid-cols-2\",\n menuLink:\n \"group/link flex items-start gap-3 rounded-lg px-2.5 py-2 text-left no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring data-active:bg-accent-soft\",\n menuIcon:\n \"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border-default bg-surface-2 text-fg-accent \" +\n \"transition-[transform,border-color] duration-300 group-hover/link:-translate-y-0.5 group-hover/link:border-accent [&>svg]:h-4 [&>svg]:w-4 motion-reduce:transform-none\",\n menuTitle: \"block line-clamp-2 wrap-break-word text-sm font-semibold leading-5 text-fg\",\n // Two lines at most, wrapped at word boundaries — a longer one ends in an ellipsis, never cut mid-sentence.\n menuText: \"mt-0.5 block line-clamp-2 wrap-break-word text-xs leading-4 text-fg-secondary\",\n actions: \"ml-auto flex items-center gap-2\",\n toggle:\n \"inline-flex h-9 w-9 items-center justify-center rounded-full border border-border-subtle bg-surface-2/60 text-fg md:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n mobile: \"kz-pop absolute inset-x-0 top-full mt-2 grid gap-1 rounded-2xl border border-border-default bg-surface-1 p-2 shadow-lg md:hidden\",\n mobileGroup: \"grid gap-0.5 [&+&]:mt-1 [&+&]:border-t [&+&]:border-border-subtle [&+&]:pt-2\",\n mobileGroupTitle: \"px-3 pb-1 pt-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n mobileLink:\n \"flex items-center justify-between rounded-xl px-3 py-3 text-[15px] font-medium text-fg no-underline hover:bg-surface-2 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:text-fg-accent\",\n // The family switcher: a chevron attached to the brand, its panel aligned on the bar's left edge.\n family: \"hidden md:block\",\n familyTrigger:\n \"group/trigger -ml-2 mr-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-fg-secondary transition-colors \" +\n \"hover:bg-surface-3 hover:text-fg data-[state=open]:bg-surface-3 data-[state=open]:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n familyViewport:\n \"kz-pop absolute left-0 top-full mt-2 h-(--radix-navigation-menu-viewport-height) w-(--radix-navigation-menu-viewport-width) origin-top-left overflow-hidden \" +\n \"rounded-2xl border border-border-default bg-surface-1 shadow-lg transition-[width,height] duration-300 motion-reduce:transition-none\",\n familyPanel: \"grid w-[min(92vw,42rem)] gap-x-2 gap-y-3 p-3 sm:grid-cols-2\",\n familyGroupTitle: \"px-3 pb-1 pt-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n familyLink:\n \"group/link flex items-start gap-3 rounded-xl px-3 py-2.5 text-left no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring data-current:bg-accent-soft\",\n familyMark: \"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center text-fg\",\n familyName: \"flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm font-semibold text-fg\",\n familyText: \"mt-0.5 block text-xs leading-relaxed text-fg-secondary\",\n familyTag: \"rounded-full px-1.5 py-px text-[10px] font-semibold uppercase tracking-wider\",\n mobileFamily: \"mt-1 grid gap-2 border-t border-border-subtle px-1 pb-1 pt-3\",\n mobileFamilyLink:\n \"flex min-h-11 items-center gap-3 rounded-xl px-2 py-2 text-[15px] font-medium text-fg no-underline hover:bg-surface-2 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-current:bg-accent-soft\",\n badge:\n \"hidden h-6 shrink-0 items-center gap-1 rounded-full border border-border-subtle bg-surface-2 px-2 font-mono text-[11px] font-medium text-fg-secondary sm:inline-flex\",\n repo:\n \"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n floating: {\n true: { root: \"max-w-5xl rounded-full border pl-5 pr-2 data-compact:max-w-4xl\" },\n false: { root: \"border-b px-4 md:px-6\" },\n },\n },\n defaultVariants: { floating: true },\n});\nconst s = navBar();\n\ninterface NavBarContext {\n label: string;\n open: boolean;\n setOpen: (open: boolean) => void;\n panelId: string;\n}\nconst Context = React.createContext<NavBarContext | null>(null);\n/** The open menu of `NavBar.Items` (`\"\"`: none), so a plain link can close it when the pointer moves on to it. */\nconst ItemsContext = React.createContext<{ close: () => void }>({ close: () => {} });\n/** True inside `NavBar.Mobile`: the family renders as the panel's last group instead of a switcher. */\nconst MobileContext = React.createContext(false);\nconst useNavBar = () => {\n const ctx = React.useContext(Context);\n if (!ctx) throw new Error(\"NavBar parts must be inside <NavBar.Root>.\");\n return ctx;\n};\n\nexport type NavBarRootProps = React.ComponentProps<\"header\"> & {\n /** The accessible name of the navigation (“Main”) — passed translated. */\n label: string;\n /**\n * Pixels of scroll after which the bar tightens (denser, lower, lifted). `false`: never.\n * @default 24\n */\n compactAfter?: number | false;\n /**\n * A floating pill (the Krizaka bar) or a full-width bar with a hairline under it.\n * @default true\n */\n floating?: boolean;\n};\n\n/** The bar. Place it in a `sticky top-3` (floating) or `sticky top-0` wrapper — or let it scroll. */\nexport function NavBarRoot({ label, compactAfter = 24, floating = true, className, children, ...props }: NavBarRootProps) {\n const [compact, setCompact] = React.useState(false);\n const [open, setOpen] = React.useState(false);\n const panelId = React.useId();\n React.useEffect(() => {\n if (compactAfter === false) return;\n const onScroll = () => setCompact(window.scrollY > compactAfter);\n onScroll();\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => window.removeEventListener(\"scroll\", onScroll);\n }, [compactAfter]);\n const ctx = React.useMemo(() => ({ label, open, setOpen, panelId }), [label, open, panelId]);\n return (\n <Context.Provider value={ctx}>\n <header data-compact={compact ? \"\" : undefined} data-floating={floating} className={navBar({ floating }).root({ className })} {...props}>\n {children}\n </header>\n </Context.Provider>\n );\n}\n\nexport type NavBarBrandProps = React.ComponentProps<\"a\"> & {\n /** Renders your link (a router `<Link>`) with the brand's look. */\n asChild?: boolean;\n};\n\n/** The brand: a mark and a name, linking home. */\nexport function NavBarBrand({ asChild, className, ...props }: NavBarBrandProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp className={s.brand({ className })} {...props} />;\n}\n\nexport type NavBarItemsProps = Omit<React.ComponentProps<typeof Nav.Root>, \"orientation\">;\n\n/**\n * The desktop navigation: links and menus, with the light that glides between them. Hidden below `md` (see\n * `NavBar.Mobile`). A menu opens on click, Enter, Space or ↓ — or when the pointer rests on its trigger (hover intent,\n * `delayDuration`, 200 ms): passing over the bar opens nothing. While a menu is open the light stays on its trigger;\n * moving on to a plain link closes the menu, so the bar never shows two current items. A click outside or Escape closes.\n */\nexport function NavBarItems({ className, children, value: valueProp, defaultValue, onValueChange, delayDuration = 200, ...props }: NavBarItemsProps) {\n const { label } = useNavBar();\n const [inner, setInner] = React.useState(defaultValue ?? \"\");\n const value = valueProp ?? inner;\n const setValue = React.useCallback(\n (next: string) => {\n setInner(next);\n onValueChange?.(next);\n },\n [onValueChange],\n );\n const items = React.useMemo(() => ({ close: () => setValue(\"\") }), [setValue]);\n const { ref, handlers } = useSlidingIndicator<HTMLUListElement>({\n item: \"[data-nav-item]\",\n active: \"[data-nav-item][data-active]\",\n prefer: \"[data-nav-item][data-state=open]\",\n follow: true,\n });\n const root = React.useRef<HTMLElement>(null);\n const viewport = React.useRef<HTMLDivElement>(null);\n // The open panel sits under its trigger and stays inside the page (collision): measured when a menu opens, when\n // the panel changes size (another menu) and when the window does.\n React.useLayoutEffect(() => {\n const panel = viewport.current;\n const nav = root.current;\n if (!panel || !nav || !value) return;\n const place = () => {\n const trigger = nav.querySelector<HTMLElement>(\"[data-nav-item][data-state=open]\");\n const width = Number.parseFloat(panel.style.getPropertyValue(\"--radix-navigation-menu-viewport-width\"));\n if (!trigger || !Number.isFinite(width)) return;\n const left = `${placePanel({ trigger: trigger.getBoundingClientRect().left, root: nav.getBoundingClientRect().left, panel: width, window: document.documentElement.clientWidth })}px`;\n if (panel.style.getPropertyValue(\"--kz-menu-left\") !== left) panel.style.setProperty(\"--kz-menu-left\", left);\n };\n place();\n const watch = new MutationObserver(place);\n watch.observe(panel, { attributes: true, attributeFilter: [\"style\"] });\n window.addEventListener(\"resize\", place);\n return () => {\n watch.disconnect();\n window.removeEventListener(\"resize\", place);\n };\n }, [value]);\n return (\n <ItemsContext.Provider value={items}>\n <Nav.Root ref={root} aria-label={label} className={s.items({ className })} value={value} onValueChange={setValue} delayDuration={delayDuration} {...props}>\n <Nav.List ref={ref} className={s.list()} {...handlers}>\n {children}\n </Nav.List>\n <Nav.Viewport ref={viewport} className={s.viewport()} />\n </Nav.Root>\n </ItemsContext.Provider>\n );\n}\n\nexport type NavBarLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"active\"> & {\n /** The current page: `aria-current=\"page\"`, and the light rests on it. */\n active?: boolean;\n /** Classes on the list item around the link (to show it from a breakpoint only). */\n itemClassName?: string;\n};\n\n/** A link of the bar. `asChild` renders your router's `<Link>`. */\nexport function NavBarLink({ active, className, itemClassName, onPointerEnter, ...props }: NavBarLinkProps) {\n const { close } = React.useContext(ItemsContext);\n return (\n <Nav.Item className={itemClassName}>\n <Nav.Link\n data-nav-item=\"\"\n data-active={active ? \"\" : undefined}\n active={active}\n aria-current={active ? \"page\" : undefined}\n className={s.item({ className })}\n onPointerEnter={(e) => {\n onPointerEnter?.(e);\n close();\n }}\n {...props}\n />\n </Nav.Item>\n );\n}\n\nexport type NavBarMenuProps = Omit<React.ComponentProps<typeof Nav.Item>, \"children\"> & {\n /** The trigger's words (“Products”) — passed translated. */\n label: string;\n /** The current page is inside this menu: the light rests on its trigger. */\n active?: boolean;\n /** The menu's links (`NavBar.MenuLink`), laid out in a panel; `columns` sets the grid. */\n children: React.ReactNode;\n /**\n * Columns of the panel.\n * @default 1\n */\n columns?: 1 | 2;\n /**\n * A section with this many links or fewer needs no hidden list: they sit in the bar as plain links (the active one\n * marked, no dropdown, `label` unused on the desktop). Above it, a dropdown. `0` always makes a dropdown.\n * @default 3\n */\n inlineUpTo?: number;\n /**\n * From which breakpoint the inline links stand in the bar. Below it (between the phone panel at `md` and this\n * breakpoint) the section is the dropdown, so long labels never overflow a tablet-width bar. `md` inlines as soon as\n * the bar is shown.\n * @default \"lg\"\n */\n inlineFrom?: \"md\" | \"lg\" | \"xl\";\n};\n\n// Static class names (Tailwind reads them at build time).\nconst INLINE_HIDE = { md: \"\", lg: \"max-lg:hidden\", xl: \"max-xl:hidden\" } as const;\nconst DROPDOWN_HIDE = { md: \"\", lg: \"lg:hidden\", xl: \"xl:hidden\" } as const;\n\n/**\n * A menu of the bar: its trigger, and its links in a panel that opens under the bar. A menu of `inlineUpTo` links or\n * fewer (3 by default) is not a menu at all: its links stand in the bar, each with its active state — a hidden list of\n * three is one click more for nothing. The phone's panel groups them as usual (`NavBar.MobileGroup`).\n */\nexport function NavBarMenu({ label, active, columns = 1, inlineUpTo = 3, inlineFrom = \"lg\", children, className, ...props }: NavBarMenuProps) {\n const links = React.Children.toArray(children);\n const dropdown = (hide: string) => (\n <Nav.Item className={cn(hide, className)} {...props}>\n <Nav.Trigger data-nav-item=\"\" data-active={active ? \"\" : undefined} className={cn(s.item(), \"group/trigger\")}>\n {label}\n <svg viewBox=\"0 0 24 24\" className={s.chevron()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </Nav.Trigger>\n <Nav.Content className={cn(s.panel(), columns === 2 && s.panelWide())}>{children}</Nav.Content>\n </Nav.Item>\n );\n const inline = links.length > 0 && links.length <= inlineUpTo && links.every((c) => React.isValidElement(c) && c.type === NavBarMenuLink);\n if (inline) {\n return (\n <>\n {inlineFrom !== \"md\" && dropdown(DROPDOWN_HIDE[inlineFrom])}\n {links.map((link) => {\n const { title, href, active: here, asChild, children: child, icon: _icon, description: _description, className: linkClass, ...rest } = (link as React.ReactElement<NavBarMenuLinkProps>).props;\n return (\n <NavBarLink key={(link as React.ReactElement).key} asChild={asChild} href={asChild ? undefined : href} active={here} className={linkClass} itemClassName={INLINE_HIDE[inlineFrom]} {...rest}>\n {asChild && React.isValidElement<{ children?: React.ReactNode }>(child) ? React.cloneElement(child, undefined, title) : title}\n </NavBarLink>\n );\n })}\n </>\n );\n }\n return dropdown(\"\");\n}\n\nexport type NavBarMenuLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"title\" | \"active\"> & {\n /** The link's name — passed translated. */\n title: string;\n /** One line under it — passed translated. */\n description?: string;\n /** An icon (a signature icon of `@krizaka/icons`: its node lights up on hover). */\n icon?: React.ReactNode;\n /** The current page. */\n active?: boolean;\n};\n\n/** A link inside a menu: an icon, a name and a line of text. */\nexport function NavBarMenuLink({ title, description, icon, active, href, className, asChild, children, ...props }: NavBarMenuLinkProps) {\n const body = (\n <>\n {icon && <span className={s.menuIcon()}>{icon}</span>}\n <span className=\"min-w-0 flex-1\">\n <span className={s.menuTitle()}>{title}</span>\n {description && <span className={s.menuText()}>{description}</span>}\n </span>\n </>\n );\n return (\n <Nav.Link asChild={asChild} href={asChild ? undefined : href} active={active} aria-current={active ? \"page\" : undefined} data-active={active ? \"\" : undefined} className={s.menuLink({ className })} {...props}>\n {asChild && React.isValidElement<{ children?: React.ReactNode }>(children) ? React.cloneElement(children, undefined, body) : body}\n </Nav.Link>\n );\n}\n\n/** The right side: search, theme, language, the call to action. */\nexport function NavBarActions({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.actions({ className })} {...props} />;\n}\n\nexport type NavBarMobileProps = React.ComponentProps<\"nav\"> & {\n /** The toggle's name when closed (“Open the menu”) — passed translated. */\n openLabel: string;\n /** The toggle's name when open (“Close the menu”) — passed translated. */\n closeLabel: string;\n};\n\n/**\n * Below `md`: a toggle, and a panel under the bar with its links (`NavBar.MobileLink`), grouped like the bar's menus\n * (`NavBar.MobileGroup`). A disclosure: Escape, a click on a link or a press outside close it; Escape returns the focus\n * to the toggle.\n */\nexport function NavBarMobile({ openLabel, closeLabel, className, children, ...props }: NavBarMobileProps) {\n const { label, open, setOpen, panelId } = useNavBar();\n const toggle = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n setOpen(false);\n toggle.current?.focus();\n };\n // A press outside the panel and its toggle closes it (the page under it stays usable).\n const onPress = (e: PointerEvent) => {\n const target = e.target as Node | null;\n if (!target || toggle.current?.contains(target) || document.getElementById(panelId)?.contains(target)) return;\n setOpen(false);\n };\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(\"pointerdown\", onPress);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(\"pointerdown\", onPress);\n };\n }, [open, setOpen, panelId]);\n return (\n <>\n <button ref={toggle} type=\"button\" aria-expanded={open} aria-controls={panelId} aria-label={open ? closeLabel : openLabel} onClick={() => setOpen(!open)} className={s.toggle()}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4.5 w-4.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n {open ? <path d=\"M18 6 6 18M6 6l12 12\" /> : <path d=\"M4 7h16M4 12h16M4 17h10\" />}\n </svg>\n </button>\n {open && (\n <nav\n id={panelId}\n aria-label={label}\n className={s.mobile({ className })}\n onClick={(e) => {\n if ((e.target as Element).closest(\"a\")) setOpen(false);\n }}\n {...props}\n >\n <MobileContext.Provider value>{children}</MobileContext.Provider>\n </nav>\n )}\n </>\n );\n}\n\nexport type NavBarMobileGroupProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The group's heading — the same words as the bar's menu (“Product”) — passed translated. */\n label: string;\n};\n\n/** A group of the mobile panel: the links of one of the bar's menus under its name, in the same order. */\nexport function NavBarMobileGroup({ label, className, children, ...props }: NavBarMobileGroupProps) {\n const id = React.useId();\n return (\n <div role=\"group\" aria-labelledby={id} className={s.mobileGroup({ className })} {...props}>\n <p id={id} className={s.mobileGroupTitle()}>\n {label}\n </p>\n {children}\n </div>\n );\n}\n\nexport type NavBarMobileLinkProps = React.ComponentProps<\"a\"> & {\n /** The current page. */\n active?: boolean;\n /** Renders your router's `<Link>`. */\n asChild?: boolean;\n};\n\n/** A link of the mobile panel. */\nexport function NavBarMobileLink({ active, asChild, className, ...props }: NavBarMobileLinkProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp aria-current={active ? \"page\" : undefined} className={s.mobileLink({ className })} {...props} />;\n}\n\n\nconst tagTone = {\n upcoming: \"bg-accent-soft text-fg ring-1 ring-inset ring-accent/40\",\n construction: \"bg-warning/15 text-fg ring-1 ring-inset ring-warning/40\",\n development: \"bg-surface-3 text-fg ring-1 ring-inset ring-border-strong\",\n current: \"bg-surface-3 text-fg-secondary\",\n};\n\n/** A site's name and its tags: status when not released, “You are here” on the current one. */\nfunction FamilyName({ site, current, labels }: { site: Site; current: boolean; labels: FamilyLabels }) {\n return (\n <span className={s.familyName()}>\n {site.name}\n {site.status !== \"released\" && <span className={cn(s.familyTag(), tagTone[site.status])}>{labels.status[site.status]}</span>}\n {current && <span className={cn(s.familyTag(), tagTone.current)}>{labels.current}</span>}\n </span>\n );\n}\n\nexport type NavBarFamilyProps = {\n /** The site this bar belongs to (`@krizaka/ui/sites`): marked in the family, `aria-current=\"true\"`. */\n current: SiteId;\n /**\n * The family's words, passed translated: `familyLabels.fr` from `@krizaka/ui/sites`, or your own of the same shape.\n * @default familyLabels.en\n */\n labels?: FamilyLabels;\n /** Opens the panel at first render (a story, a screenshot). */\n defaultOpen?: boolean;\n /** Classes of the switcher (or, inside `NavBar.Mobile`, of the family's group). */\n className?: string;\n};\n\n/**\n * The family switcher, attached to the brand (place it right after `NavBar.Brand`): a chevron that opens every site of\n * Krizaka grouped — Krizaka · Products · Open source · GitHub — the current one marked. It opens on click, Enter, Space\n * or ↓ (never on hover); ↑ ↓ walk the sites, Escape closes and returns the focus. Hidden below `md`: place a second\n * `NavBar.Family` as the last child of `NavBar.Mobile`, where it renders as the panel's last group.\n */\nexport function NavBarFamily({ current, labels = familyLabels.en, defaultOpen, className }: NavBarFamilyProps) {\n const inMobile = React.useContext(MobileContext);\n const id = React.useId();\n const groups = sitesByGroup();\n if (inMobile) {\n return (\n <div role=\"group\" aria-labelledby={`${id}-family`} className={s.mobileFamily({ className })}>\n <p id={`${id}-family`} className={s.familyGroupTitle()}>\n {labels.family}\n </p>\n {groups.map(({ group, sites }) => (\n <div key={group}>\n {/* The company's own group is named like the family itself: its heading would repeat the panel's. */}\n {group !== \"krizaka\" && (\n <p id={`${id}-${group}`} className={s.familyGroupTitle()}>\n {labels.groups[group]}\n </p>\n )}\n <ul aria-labelledby={group !== \"krizaka\" ? `${id}-${group}` : `${id}-family`} className=\"grid gap-0.5\">\n {sites.map((site) => (\n <li key={site.id}>\n <a href={site.url} data-current={site.id === current ? \"\" : undefined} aria-current={site.id === current ? \"true\" : undefined} className={s.mobileFamilyLink()}>\n <span className={s.familyMark()}>\n <FamilyMark mark={site.mark} size={24} />\n </span>\n <FamilyName site={site} current={site.id === current} labels={labels} />\n </a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n );\n }\n // Click only: a pointer passing over the brand never opens the family (Radix opens its menus on hover by default).\n const noHover = (e: React.PointerEvent) => e.preventDefault();\n const columns = [groups.filter((g) => g.group === \"krizaka\" || g.group === \"products\"), groups.filter((g) => g.group === \"open-source\" || g.group === \"github\")];\n return (\n <Nav.Root aria-label={labels.family} defaultValue={defaultOpen ? \"family\" : undefined} className={s.family({ className })}>\n <Nav.List className=\"flex\">\n <Nav.Item value=\"family\">\n <Nav.Trigger aria-label={labels.family} onPointerMove={noHover} onPointerLeave={noHover} className={s.familyTrigger()}>\n <svg viewBox=\"0 0 24 24\" className={s.chevron()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </Nav.Trigger>\n <Nav.Content onPointerLeave={noHover} className={s.familyPanel()}>\n {columns.map((column, i) => (\n <div key={i} className=\"grid content-start gap-3\">\n {column.map(({ group, sites }) => (\n <div key={group}>\n <p id={`${id}-${group}`} className={s.familyGroupTitle()}>\n {labels.groups[group]}\n </p>\n <ul aria-labelledby={`${id}-${group}`} className=\"grid gap-0.5\">\n {sites.map((site) => (\n <li key={site.id}>\n <Nav.Link href={site.url} data-current={site.id === current ? \"\" : undefined} aria-current={site.id === current ? \"true\" : undefined} className={s.familyLink()}>\n <span className={s.familyMark()}>\n <FamilyMark mark={site.mark} size={28} />\n </span>\n <span className=\"min-w-0\">\n <FamilyName site={site} current={site.id === current} labels={labels} />\n <span className={s.familyText()}>{labels.descriptions[site.id]}</span>\n </span>\n </Nav.Link>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n ))}\n </Nav.Content>\n </Nav.Item>\n </Nav.List>\n <Nav.Viewport onPointerLeave={noHover} className={s.familyViewport()} />\n </Nav.Root>\n );\n}\n\nexport type NavBarBadgeProps = React.ComponentProps<\"span\"> & {\n /** The environment, when it is not production (`DEV`). */\n env?: string;\n /** The version the site runs (`1.2.3`, shown `v1.2.3`). */\n version?: string;\n};\n\n/** A small mono badge beside the brand: the environment and the version (`DEV · v1.2.3`). Hidden below `sm`. */\nexport function NavBarBadge({ env, version, className, children, ...props }: NavBarBadgeProps) {\n const parts = [env, version && `v${version.replace(/^v/, \"\")}`].filter(Boolean);\n return (\n <span className={s.badge({ className })} {...props}>\n {parts.join(\" · \")}\n {children}\n </span>\n );\n}\n\nexport type NavBarLanguageProps = Omit<LanguageSwitchProps, \"variant\">;\n\n/**\n * The language switch, the same on every site: in the bar a globe and the current code (FR) opening a short menu of\n * the languages by their own names; inside `NavBar.Mobile` the same languages as a row in the preferences.\n */\nexport function NavBarLanguage(props: NavBarLanguageProps) {\n const inMobile = React.useContext(MobileContext);\n return <LanguageSwitch variant={inMobile ? \"inline\" : \"menu\"} {...props} />;\n}\n\nexport type NavBarMobilePreferencesProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The group's heading (“Preferences”) — passed translated. */\n label: string;\n};\n\n/** The last group of the mobile panel: the preferences (theme, `NavBar.Language`), in a row under their heading. */\nexport function NavBarMobilePreferences({ label, className, children, ...props }: NavBarMobilePreferencesProps) {\n const id = React.useId();\n return (\n <div role=\"group\" aria-labelledby={id} className={s.mobileGroup({ className })} {...props}>\n <p id={id} className={s.mobileGroupTitle()}>\n {label}\n </p>\n <div className=\"flex flex-wrap items-center gap-2 px-3 py-1\">{children}</div>\n </div>\n );\n}\n\nexport type NavBarRepoProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The repository the site is built from. */\n href: string;\n /** The link's accessible name (“Source on GitHub”) — passed translated. */\n label: string;\n};\n\n/** The site's repository on GitHub, an icon link of `NavBar.Actions`. */\nexport function NavBarRepo({ label, className, ...props }: NavBarRepoProps) {\n return (\n <a aria-label={label} className={s.repo({ className })} {...props}>\n <GitHubGlyph size={18} />\n </a>\n );\n}\n\n/**\n * `NavBar.Root/Brand/Family/Badge/Items/Link/Menu/MenuLink/Actions/Repo/Language/Mobile/MobileGroup/MobilePreferences/MobileLink` — the Krizaka navigation bar,\n * the same on every site of the family: floating, tightening on scroll, the family switcher on the brand, one light\n * gliding between its items, accessible menus, a mobile panel.\n */\nexport const NavBar = {\n Root: NavBarRoot,\n Brand: NavBarBrand,\n Family: NavBarFamily,\n Badge: NavBarBadge,\n Items: NavBarItems,\n Link: NavBarLink,\n Menu: NavBarMenu,\n MenuLink: NavBarMenuLink,\n Actions: NavBarActions,\n Repo: NavBarRepo,\n Language: NavBarLanguage,\n Mobile: NavBarMobile,\n MobilePreferences: NavBarMobilePreferences,\n MobileGroup: NavBarMobileGroup,\n MobileLink: NavBarMobileLink,\n};\n"
|
|
596
610
|
},
|
|
597
611
|
{
|
|
598
612
|
"path": "nav-bar/placement.ts",
|
|
@@ -604,7 +618,7 @@
|
|
|
604
618
|
}
|
|
605
619
|
],
|
|
606
620
|
"dependencies": [
|
|
607
|
-
"@krizaka/tokens@^3.
|
|
621
|
+
"@krizaka/tokens@^3.31.0",
|
|
608
622
|
"radix-ui@^1.7.0",
|
|
609
623
|
"tailwind-variants@^3.3.1"
|
|
610
624
|
],
|
|
@@ -727,6 +741,13 @@
|
|
|
727
741
|
"default": null,
|
|
728
742
|
"description": "The current page: `aria-current=\"page\"`, and the light rests on it.",
|
|
729
743
|
"required": false
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
"name": "itemClassName",
|
|
747
|
+
"type": "string",
|
|
748
|
+
"default": null,
|
|
749
|
+
"description": "Classes on the list item around the link (to show it from a breakpoint only).",
|
|
750
|
+
"required": false
|
|
730
751
|
}
|
|
731
752
|
]
|
|
732
753
|
},
|
|
@@ -762,6 +783,13 @@
|
|
|
762
783
|
"description": "Columns of the panel.",
|
|
763
784
|
"required": false
|
|
764
785
|
},
|
|
786
|
+
{
|
|
787
|
+
"name": "inlineFrom",
|
|
788
|
+
"type": "\"md\" | \"lg\" | \"xl\"",
|
|
789
|
+
"default": "lg",
|
|
790
|
+
"description": "From which breakpoint the inline links stand in the bar. Below it (between the phone panel at `md` and this\nbreakpoint) the section is the dropdown, so long labels never overflow a tablet-width bar. `md` inlines as soon as\nthe bar is shown.",
|
|
791
|
+
"required": false
|
|
792
|
+
},
|
|
765
793
|
{
|
|
766
794
|
"name": "inlineUpTo",
|
|
767
795
|
"type": "number",
|
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
"name": "pointer-light",
|
|
3
3
|
"type": "primitive",
|
|
4
4
|
"title": "Pointer light",
|
|
5
|
-
"summary": "
|
|
6
|
-
"description": "
|
|
7
|
-
"status": "
|
|
8
|
-
"deprecated":
|
|
5
|
+
"summary": "Deprecated (krizaka-ui#393): it drew a light that followed the pointer over cards. It now does nothing — the cards have one quiet hover of their own.",
|
|
6
|
+
"description": "Deprecated (krizaka-ui#393): it drew a light that followed the pointer over cards. It now does nothing — the cards have one quiet hover of their own.",
|
|
7
|
+
"status": "deprecated",
|
|
8
|
+
"deprecated": {
|
|
9
|
+
"since": "3.31.0",
|
|
10
|
+
"removedIn": "4.0.0",
|
|
11
|
+
"use": "card-grid",
|
|
12
|
+
"reason": "krizaka-ui#393: the light that followed the pointer is retired — every card has one quiet hover (a surface and hairline step, no movement, no light). It renders a hidden span and does nothing."
|
|
13
|
+
},
|
|
9
14
|
"category": "blocks",
|
|
10
15
|
"platforms": "web",
|
|
11
16
|
"parity": {
|
|
@@ -55,20 +60,15 @@
|
|
|
55
60
|
"entry": "@krizaka/ui/pointer-light",
|
|
56
61
|
"import": "import { PointerLight, usePointerLight } from \"@krizaka/ui/pointer-light\";",
|
|
57
62
|
"props": [
|
|
58
|
-
{
|
|
59
|
-
"component": "POINTER_LIGHT_TARGET",
|
|
60
|
-
"description": "Every card of the page blocks carries it: LinkCard, ArgumentCard.",
|
|
61
|
-
"props": []
|
|
62
|
-
},
|
|
63
63
|
{
|
|
64
64
|
"component": "PointerLight",
|
|
65
|
-
"description": "
|
|
65
|
+
"description": "",
|
|
66
66
|
"props": [
|
|
67
67
|
{
|
|
68
68
|
"name": "target",
|
|
69
69
|
"type": "string",
|
|
70
70
|
"default": "[data-kz-light]",
|
|
71
|
-
"description": "
|
|
71
|
+
"description": "Ignored.",
|
|
72
72
|
"required": false
|
|
73
73
|
}
|
|
74
74
|
]
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
{
|
|
79
79
|
"name": "list",
|
|
80
80
|
"title": "A row of cards",
|
|
81
|
-
"description": "Two link cards in a flex row,
|
|
81
|
+
"description": "Two link cards in a flex row, with their own quiet hover: the deprecated PointerLight inside adds nothing.",
|
|
82
82
|
"path": "examples/pointer-light/list.tsx",
|
|
83
83
|
"module": "@krizaka/ui/registry/examples/pointer-light/list",
|
|
84
84
|
"code": "import { LinkCard } from \"@krizaka/ui/link-card\";\nimport { PointerLight } from \"@krizaka/ui/pointer-light\";\n\n// Any group of cards that is not a CardGrid (a flex row, a list): drop `PointerLight` inside it — it listens on its\n// parent and lights the card the pointer is over (`[data-kz-light]`). Nothing on touch screens or under reduced motion.\nexport default function PointerLightList() {\n return (\n <div className=\"flex w-full max-w-3xl flex-wrap gap-3\">\n <LinkCard className=\"flex-1 basis-56\" href=\"#tokens\" title=\"Tokens\" description=\"Every colour, radius and shadow.\" />\n <LinkCard className=\"flex-1 basis-56\" href=\"#preset\" title=\"Tailwind preset\" description=\"The tokens as utilities.\" />\n <PointerLight />\n </div>\n );\n}\n"
|
|
@@ -93,15 +93,15 @@
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "pointer-light/index.ts",
|
|
96
|
-
"content": "/** @krizaka/ui/pointer-light —
|
|
96
|
+
"content": "/** @krizaka/ui/pointer-light — deprecated since 3.31 (krizaka-ui#393): `PointerLight` and `usePointerLight` do nothing. */\nexport { POINTER_LIGHT_TARGET, PointerLight, type PointerLightProps, usePointerLight } from \"./pointer-light\";\n"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"path": "pointer-light/meta.ts",
|
|
100
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Pointer light\",\n summary:\n \"
|
|
100
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Pointer light\",\n summary:\n \"Deprecated (krizaka-ui#393): it drew a light that followed the pointer over cards. It now does nothing — the cards have one quiet hover of their own.\",\n status: \"deprecated\",\n deprecated: {\n since: \"3.31.0\",\n removedIn: \"4.0.0\",\n use: \"card-grid\",\n reason: \"krizaka-ui#393: the light that followed the pointer is retired — every card has one quiet hover (a surface and hairline step, no movement, no light). It renders a hidden span and does nothing.\",\n },\n category: \"blocks\",\n platforms: \"web\",\n parity: { native: { status: \"n/a\", reason: \"It follows a mouse pointer; a phone has none.\" }, desktop: { status: \"n/a\", reason: \"A public site's page, not a window of the Mac app.\" } },\n whenToUse: [\n \"Inside a group of cards that is not a `CardGrid` (a flex row, a list, a carousel).\",\n \"With `usePointerLight` in a component of your own that lays cards out.\",\n ],\n whenNotToUse: [\n { when: \"Inside a CardGrid: it lights its cards already.\", use: \"card-grid\" },\n { when: \"On one card alone, with a ring of the brand's gradient.\", use: \"glow-card\" },\n ],\n bestPractices: [\n \"One light per group: drop it once, as the last child of the group.\",\n \"The cards of the blocks carry `data-kz-light`; a card of your own adds it and draws `--kz-pointer-x/y` in its CSS.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Purely decorative: it renders a hidden, empty element and adds nothing to the accessibility tree.\",\n \"Nothing runs on touch screens or under `prefers-reduced-motion`; nothing loops.\",\n ],\n },\n related: [\"card-grid\", \"glow-card\", \"link-card\"],\n web: {\n imports: [\"PointerLight\", \"usePointerLight\"],\n examples: [{ name: \"list\", title: \"A row of cards\", description: \"Two link cards in a flex row, with their own quiet hover: the deprecated PointerLight inside adds nothing.\" }],\n },\n} satisfies ComponentMeta;\n"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"path": "pointer-light/pointer-light.tsx",
|
|
104
|
-
"content": "//
|
|
104
|
+
"content": "// Deprecated (krizaka-ui#393): the light that followed the pointer over the cards is retired — the family's hover is\n// quiet (a border and a surface step, no movement, no light). The exports stay so nothing breaks; they do nothing.\nimport type * as React from \"react\";\n\n/** @deprecated Since 3.31: no card carries it any more. */\nexport const POINTER_LIGHT_TARGET = \"[data-kz-light]\";\n\n/** @deprecated Since 3.31 (krizaka-ui#393): does nothing. Removed in 4.0. */\nexport function usePointerLight(_ref: React.RefObject<HTMLElement | null>, _target: string = POINTER_LIGHT_TARGET): void {}\n\nexport type PointerLightProps = {\n /**\n * Ignored.\n * @default \"[data-kz-light]\"\n */\n target?: string;\n};\n\n/** @deprecated Since 3.31 (krizaka-ui#393): renders an empty hidden span and nothing else. Remove it. Removed in 4.0. */\nexport function PointerLight(_props: PointerLightProps) {\n return <span hidden />;\n}\n"
|
|
105
105
|
}
|
|
106
106
|
],
|
|
107
107
|
"dependencies": [],
|
|
@@ -111,20 +111,15 @@
|
|
|
111
111
|
"content": "import { LinkCard } from \"@krizaka/ui/link-card\";\nimport { PointerLight } from \"@krizaka/ui/pointer-light\";\n\n// Any group of cards that is not a CardGrid (a flex row, a list): drop `PointerLight` inside it — it listens on its\n// parent and lights the card the pointer is over (`[data-kz-light]`). Nothing on touch screens or under reduced motion.\nexport default function PointerLightList() {\n return (\n <div className=\"flex w-full max-w-3xl flex-wrap gap-3\">\n <LinkCard className=\"flex-1 basis-56\" href=\"#tokens\" title=\"Tokens\" description=\"Every colour, radius and shadow.\" />\n <LinkCard className=\"flex-1 basis-56\" href=\"#preset\" title=\"Tailwind preset\" description=\"The tokens as utilities.\" />\n <PointerLight />\n </div>\n );\n}\n"
|
|
112
112
|
},
|
|
113
113
|
"props": [
|
|
114
|
-
{
|
|
115
|
-
"component": "POINTER_LIGHT_TARGET",
|
|
116
|
-
"description": "Every card of the page blocks carries it: LinkCard, ArgumentCard.",
|
|
117
|
-
"props": []
|
|
118
|
-
},
|
|
119
114
|
{
|
|
120
115
|
"component": "PointerLight",
|
|
121
|
-
"description": "
|
|
116
|
+
"description": "",
|
|
122
117
|
"props": [
|
|
123
118
|
{
|
|
124
119
|
"name": "target",
|
|
125
120
|
"type": "string",
|
|
126
121
|
"default": "[data-kz-light]",
|
|
127
|
-
"description": "
|
|
122
|
+
"description": "Ignored.",
|
|
128
123
|
"required": false
|
|
129
124
|
}
|
|
130
125
|
]
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"why": "A buyer compares three plans side by side and answers the last objections before choosing.",
|
|
144
144
|
"path": "examples/pricing-page/credits.tsx",
|
|
145
145
|
"module": "@krizaka/ui/registry/examples/pricing-page/credits",
|
|
146
|
-
"code": "import { Button } from \"@krizaka/ui/button\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { PricingPage } from \"@krizaka/ui/pricing-page\";\n\n// TEMPLATE — plans and credits (Orazaka): the plans side by side, the featured one lifted; the comparison scrolls on a\n// phone; what a credit buys; the questions people ask, folded.\nexport default function PricingPageCredits() {\n return (\n <PricingPage\n brand=\"orazaka\"\n eyebrow=\"Pricing\"\n title=\"Pay once for what was done\"\n lead=\"Credits buy answers, images and minutes of video — never a seat you do not use.\"\n plansLabel=\"Plans\"\n plans={[\n { id: \"community\", name: \"Community\", price: \"Free\", unit: \"self-hosted\", features: [\"Every feature\", \"Your hardware\"], action: <Button variant=\"secondary\"
|
|
146
|
+
"code": "import { Button } from \"@krizaka/ui/button\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { PricingPage } from \"@krizaka/ui/pricing-page\";\n\n// TEMPLATE — plans and credits (Orazaka): the plans side by side, the featured one lifted; the comparison scrolls on a\n// phone; what a credit buys; the questions people ask, folded.\nexport default function PricingPageCredits() {\n return (\n <PricingPage\n brand=\"orazaka\"\n eyebrow=\"Pricing\"\n title=\"Pay once for what was done\"\n lead=\"Credits buy answers, images and minutes of video — never a seat you do not use.\"\n plansLabel=\"Plans\"\n plans={[\n { id: \"community\", name: \"Community\", price: \"Free\", unit: \"self-hosted\", features: [\"Every feature\", \"Your hardware\"], action: <Button variant=\"secondary\" className=\"w-full\">Install</Button> },\n { id: \"workspace\", name: \"Workspace\", price: \"CA$29\", unit: \"per month · 12,000 credits\", features: [\"Packs & Studios\", \"Shared workspaces\"], featured: true, badge: \"Most chosen\", action: <Button variant=\"primary\" className=\"w-full\">Choose Workspace</Button> },\n { id: \"sovereign\", name: \"Sovereign\", price: \"Custom\", unit: \"on your servers\", features: [\"Installed with you\", \"A named engineer\"], action: <Button variant=\"secondary\" className=\"w-full\">Talk to us</Button> },\n ]}\n compare={{\n caption: \"Compare the plans\",\n columns: [\"Community\", \"Workspace\", \"Sovereign\"],\n rows: [\n { feature: \"Local models\", cells: [\"✓\", \"✓\", \"✓\"] },\n { feature: \"Shared workspaces\", cells: [\"—\", \"✓\", \"✓\"] },\n { feature: \"Support\", cells: [\"Community\", \"Email\", \"Named engineer\"] },\n ],\n }}\n credits={{ title: \"What a credit buys\", children: <div className=\"max-w-md\"><Meter label=\"Credits this month\" value={3800} max={12000} valueText=\"3,800 of 12,000 used\" showLabel /><p className=\"mt-3 text-sm text-fg-secondary\">1 credit ≈ a chat answer · 4 an image · 30 a minute of video.</p></div> }}\n faq={{ title: \"Questions\", items: [{ question: \"What happens when credits run out?\", answer: \"Nothing breaks: requests wait until the next month or a top-up.\" }, { question: \"Do credits expire?\", answer: \"Unused credits roll over for one month.\" }] }}\n />\n );\n}\n"
|
|
147
147
|
}
|
|
148
148
|
]
|
|
149
149
|
},
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
],
|
|
179
179
|
"demo": {
|
|
180
180
|
"path": "examples/pricing-page/credits.tsx",
|
|
181
|
-
"content": "import { Button } from \"@krizaka/ui/button\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { PricingPage } from \"@krizaka/ui/pricing-page\";\n\n// TEMPLATE — plans and credits (Orazaka): the plans side by side, the featured one lifted; the comparison scrolls on a\n// phone; what a credit buys; the questions people ask, folded.\nexport default function PricingPageCredits() {\n return (\n <PricingPage\n brand=\"orazaka\"\n eyebrow=\"Pricing\"\n title=\"Pay once for what was done\"\n lead=\"Credits buy answers, images and minutes of video — never a seat you do not use.\"\n plansLabel=\"Plans\"\n plans={[\n { id: \"community\", name: \"Community\", price: \"Free\", unit: \"self-hosted\", features: [\"Every feature\", \"Your hardware\"], action: <Button variant=\"secondary\"
|
|
181
|
+
"content": "import { Button } from \"@krizaka/ui/button\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { PricingPage } from \"@krizaka/ui/pricing-page\";\n\n// TEMPLATE — plans and credits (Orazaka): the plans side by side, the featured one lifted; the comparison scrolls on a\n// phone; what a credit buys; the questions people ask, folded.\nexport default function PricingPageCredits() {\n return (\n <PricingPage\n brand=\"orazaka\"\n eyebrow=\"Pricing\"\n title=\"Pay once for what was done\"\n lead=\"Credits buy answers, images and minutes of video — never a seat you do not use.\"\n plansLabel=\"Plans\"\n plans={[\n { id: \"community\", name: \"Community\", price: \"Free\", unit: \"self-hosted\", features: [\"Every feature\", \"Your hardware\"], action: <Button variant=\"secondary\" className=\"w-full\">Install</Button> },\n { id: \"workspace\", name: \"Workspace\", price: \"CA$29\", unit: \"per month · 12,000 credits\", features: [\"Packs & Studios\", \"Shared workspaces\"], featured: true, badge: \"Most chosen\", action: <Button variant=\"primary\" className=\"w-full\">Choose Workspace</Button> },\n { id: \"sovereign\", name: \"Sovereign\", price: \"Custom\", unit: \"on your servers\", features: [\"Installed with you\", \"A named engineer\"], action: <Button variant=\"secondary\" className=\"w-full\">Talk to us</Button> },\n ]}\n compare={{\n caption: \"Compare the plans\",\n columns: [\"Community\", \"Workspace\", \"Sovereign\"],\n rows: [\n { feature: \"Local models\", cells: [\"✓\", \"✓\", \"✓\"] },\n { feature: \"Shared workspaces\", cells: [\"—\", \"✓\", \"✓\"] },\n { feature: \"Support\", cells: [\"Community\", \"Email\", \"Named engineer\"] },\n ],\n }}\n credits={{ title: \"What a credit buys\", children: <div className=\"max-w-md\"><Meter label=\"Credits this month\" value={3800} max={12000} valueText=\"3,800 of 12,000 used\" showLabel /><p className=\"mt-3 text-sm text-fg-secondary\">1 credit ≈ a chat answer · 4 an image · 30 a minute of video.</p></div> }}\n faq={{ title: \"Questions\", items: [{ question: \"What happens when credits run out?\", answer: \"Nothing breaks: requests wait until the next month or a top-up.\" }, { question: \"Do credits expire?\", answer: \"Unused credits roll over for one month.\" }] }}\n />\n );\n}\n"
|
|
182
182
|
},
|
|
183
183
|
"props": [
|
|
184
184
|
{
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"why": "A visitor understands what a product does, the traps it avoids and the decisions behind it, then acts.",
|
|
180
180
|
"path": "examples/product-page/orazaka.tsx",
|
|
181
181
|
"module": "@krizaka/ui/registry/examples/product-page/orazaka",
|
|
182
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\nimport { ProductPage } from \"@krizaka/ui/product-page\";\n\n// TEMPLATE — a product's overview (Orazaka, in its orange): the promise beside the console alive, the traps it removes\n// as compact cards with their bird, its decisions and what they cost, the owl's promise, the closing call.\nexport default function ProductPageOrazaka() {\n return (\n <ProductPage\n brand=\"orazaka\"\n mark={<><ProductLogo id=\"orazaka\" size={18} animated={false} /> Orazaka · sovereign AI</>}\n title=\"The AI that never leaves home\"\n lead=\"Chat, image, voice and video on your own server — one governed pipeline, nothing sent anywhere.\"\n facts={[{ label: \"Apache-2.0\", tone: \"ok\" }, { label: \"Law 25 · GDPR\" }]}\n actions={<Button variant=\"primary\" size=\"lg\"
|
|
182
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\nimport { ProductPage } from \"@krizaka/ui/product-page\";\n\n// TEMPLATE — a product's overview (Orazaka, in its orange): the promise beside the console alive, the traps it removes\n// as compact cards with their bird, its decisions and what they cost, the owl's promise, the closing call.\nexport default function ProductPageOrazaka() {\n return (\n <ProductPage\n brand=\"orazaka\"\n mark={<><ProductLogo id=\"orazaka\" size={18} animated={false} /> Orazaka · sovereign AI</>}\n title=\"The AI that never leaves home\"\n lead=\"Chat, image, voice and video on your own server — one governed pipeline, nothing sent anywhere.\"\n facts={[{ label: \"Apache-2.0\", tone: \"ok\" }, { label: \"Law 25 · GDPR\" }]}\n actions={<Button variant=\"primary\" size=\"lg\">Install in three commands</Button>}\n visualLabel=\"orazaka.local — chat\"\n visual={<p className=\"p-6 text-sm text-fg-secondary\">“Summarise the lease in plain French.” — answered locally in 1.8 s.</p>}\n reasonsHeading={{ eyebrow: \"Why\", title: \"Three traps it removes\" }}\n reasonLabels={{ trap: \"The trap\", more: \"Details\" }}\n reasons={[\n { id: \"local\", title: \"Local inference\", trap: \"Elsewhere, every prompt crosses the ocean.\", mark: <Mascot bird=\"falcon\" size={40} animated={false} />, href: \"#local\" },\n { id: \"paid\", title: \"Paid once\", trap: \"Elsewhere, you pay per token, forever.\", mark: <Mascot bird=\"flamingo\" size={40} animated={false} />, href: \"#paid\" },\n { id: \"asked\", title: \"Agents that ask\", trap: \"Elsewhere, an agent acts first.\", mark: <Mascot bird=\"pigeon\" size={40} animated={false} />, href: \"#asked\" },\n ]}\n decisions={{\n eyebrow: \"Decisions\",\n title: \"What we chose, and what it costs you\",\n labels: { chose: \"We chose\", refused: \"We refused\", because: \"Because\", cost: \"What it costs you\" },\n items: [{ id: \"local\", chose: \"Local models by default.\", refused: \"A hosted API by default.\", because: \"A prompt that never leaves cannot leak.\", cost: \"A GPU, or patience.\" }],\n }}\n promise={{ bird: \"owl\", label: \"The owl's promise\", text: \"Every personal record stays in Québec, and you can read who touched it.\" }}\n closing={{ title: \"Ready to run it at home?\", actions: <Button variant=\"primary\" size=\"lg\">Install</Button> }}\n />\n );\n}\n"
|
|
183
183
|
}
|
|
184
184
|
]
|
|
185
185
|
},
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
],
|
|
218
218
|
"demo": {
|
|
219
219
|
"path": "examples/product-page/orazaka.tsx",
|
|
220
|
-
"content": "import { ProductLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\nimport { ProductPage } from \"@krizaka/ui/product-page\";\n\n// TEMPLATE — a product's overview (Orazaka, in its orange): the promise beside the console alive, the traps it removes\n// as compact cards with their bird, its decisions and what they cost, the owl's promise, the closing call.\nexport default function ProductPageOrazaka() {\n return (\n <ProductPage\n brand=\"orazaka\"\n mark={<><ProductLogo id=\"orazaka\" size={18} animated={false} /> Orazaka · sovereign AI</>}\n title=\"The AI that never leaves home\"\n lead=\"Chat, image, voice and video on your own server — one governed pipeline, nothing sent anywhere.\"\n facts={[{ label: \"Apache-2.0\", tone: \"ok\" }, { label: \"Law 25 · GDPR\" }]}\n actions={<Button variant=\"primary\" size=\"lg\"
|
|
220
|
+
"content": "import { ProductLogo } from \"@krizaka/ui\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\nimport { ProductPage } from \"@krizaka/ui/product-page\";\n\n// TEMPLATE — a product's overview (Orazaka, in its orange): the promise beside the console alive, the traps it removes\n// as compact cards with their bird, its decisions and what they cost, the owl's promise, the closing call.\nexport default function ProductPageOrazaka() {\n return (\n <ProductPage\n brand=\"orazaka\"\n mark={<><ProductLogo id=\"orazaka\" size={18} animated={false} /> Orazaka · sovereign AI</>}\n title=\"The AI that never leaves home\"\n lead=\"Chat, image, voice and video on your own server — one governed pipeline, nothing sent anywhere.\"\n facts={[{ label: \"Apache-2.0\", tone: \"ok\" }, { label: \"Law 25 · GDPR\" }]}\n actions={<Button variant=\"primary\" size=\"lg\">Install in three commands</Button>}\n visualLabel=\"orazaka.local — chat\"\n visual={<p className=\"p-6 text-sm text-fg-secondary\">“Summarise the lease in plain French.” — answered locally in 1.8 s.</p>}\n reasonsHeading={{ eyebrow: \"Why\", title: \"Three traps it removes\" }}\n reasonLabels={{ trap: \"The trap\", more: \"Details\" }}\n reasons={[\n { id: \"local\", title: \"Local inference\", trap: \"Elsewhere, every prompt crosses the ocean.\", mark: <Mascot bird=\"falcon\" size={40} animated={false} />, href: \"#local\" },\n { id: \"paid\", title: \"Paid once\", trap: \"Elsewhere, you pay per token, forever.\", mark: <Mascot bird=\"flamingo\" size={40} animated={false} />, href: \"#paid\" },\n { id: \"asked\", title: \"Agents that ask\", trap: \"Elsewhere, an agent acts first.\", mark: <Mascot bird=\"pigeon\" size={40} animated={false} />, href: \"#asked\" },\n ]}\n decisions={{\n eyebrow: \"Decisions\",\n title: \"What we chose, and what it costs you\",\n labels: { chose: \"We chose\", refused: \"We refused\", because: \"Because\", cost: \"What it costs you\" },\n items: [{ id: \"local\", chose: \"Local models by default.\", refused: \"A hosted API by default.\", because: \"A prompt that never leaves cannot leak.\", cost: \"A GPU, or patience.\" }],\n }}\n promise={{ bird: \"owl\", label: \"The owl's promise\", text: \"Every personal record stays in Québec, and you can read who touched it.\" }}\n closing={{ title: \"Ready to run it at home?\", actions: <Button variant=\"primary\" size=\"lg\">Install</Button> }}\n />\n );\n}\n"
|
|
221
221
|
},
|
|
222
222
|
"props": [
|
|
223
223
|
{
|
package/registry/progress.json
CHANGED
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
126
|
"name": "status",
|
|
127
|
-
"type": "\"error\" | \"
|
|
127
|
+
"type": "\"error\" | \"active\" | \"paused\"",
|
|
128
128
|
"default": "active",
|
|
129
129
|
"description": "active · paused (the fill rests, striped: waiting for someone) · error (the fill turns to the danger colour where it stopped).",
|
|
130
130
|
"required": false
|
|
@@ -427,7 +427,7 @@
|
|
|
427
427
|
},
|
|
428
428
|
{
|
|
429
429
|
"name": "status",
|
|
430
|
-
"type": "\"error\" | \"
|
|
430
|
+
"type": "\"error\" | \"active\" | \"paused\"",
|
|
431
431
|
"default": "active",
|
|
432
432
|
"description": "active · paused (the fill rests, striped: waiting for someone) · error (the fill turns to the danger colour where it stopped).",
|
|
433
433
|
"required": false
|
|
@@ -331,7 +331,7 @@
|
|
|
331
331
|
},
|
|
332
332
|
{
|
|
333
333
|
"path": "radio-group/radio-group.tsx",
|
|
334
|
-
"content": "// Client (a context). Radix RadioGroup: role=\"radiogroup\" / \"radio\", one tab stop, the arrows move and select, a\n// hidden input inside a form. Four looks, one behaviour:\n// - `RadioGroup.Item`: a dot and its words (a settings list);\n// - `RadioGroup.Card`: a whole card is the radio — an amount, a pack, a plan — its check lit when chosen;\n// - `RadioGroup.Button`: a tactile key that presses in — raised on a ledge, sunk when chosen (a size, a tip, a level);\n// - `RadioGroup.Segment` in `<RadioGroup.Root variant=\"segmented\">`: a segmented control, one light gliding to the choice.\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { CheckGlyph } from \"../glyphs\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { tv } from \"../tv\";\n\nexport const radioGroup = tv({\n slots: {\n root: \"grid gap-2 data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-wrap\",\n item:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-strong bg-surface-1 inset-shadow-well transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:shadow-glow aria-invalid:border-danger\",\n dot: \"h-2 w-2 rounded-full bg-on-accent\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-60\",\n card:\n \"group relative flex min-w-0 flex-col items-start gap-1 rounded-2xl border border-border-default bg-surface-1 bg-sheen p-4 pr-10 text-left text-sm text-fg inset-shadow-edge \" +\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 hover:border-border-strong active:scale-[0.99] motion-reduce:transition-none motion-reduce:active:scale-100 \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent-soft data-[state=checked]:shadow-glow\",\n // The card's check: an empty ring at rest, the accent filled with its check once chosen.\n cardCheck:\n \"absolute right-3.5 top-3.5 inline-flex h-5 w-5 items-center justify-center rounded-full border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\n \"group-data-[state=checked]:border-accent group-data-[state=checked]:bg-accent\",\n // The tactile key: on a ledge (its lower edge),
|
|
334
|
+
"content": "// Client (a context). Radix RadioGroup: role=\"radiogroup\" / \"radio\", one tab stop, the arrows move and select, a\n// hidden input inside a form. Four looks, one behaviour:\n// - `RadioGroup.Item`: a dot and its words (a settings list);\n// - `RadioGroup.Card`: a whole card is the radio — an amount, a pack, a plan — its check lit when chosen;\n// - `RadioGroup.Button`: a tactile key that presses in — raised on a ledge, sunk when chosen (a size, a tip, a level);\n// - `RadioGroup.Segment` in `<RadioGroup.Root variant=\"segmented\">`: a segmented control, one light gliding to the choice.\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { CheckGlyph } from \"../glyphs\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { tv } from \"../tv\";\n\nexport const radioGroup = tv({\n slots: {\n root: \"grid gap-2 data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-wrap\",\n item:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-strong bg-surface-1 inset-shadow-well transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:shadow-glow aria-invalid:border-danger\",\n dot: \"h-2 w-2 rounded-full bg-on-accent\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-60\",\n card:\n \"group relative flex min-w-0 flex-col items-start gap-1 rounded-2xl border border-border-default bg-surface-1 bg-sheen p-4 pr-10 text-left text-sm text-fg inset-shadow-edge \" +\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 hover:border-border-strong active:scale-[0.99] motion-reduce:transition-none motion-reduce:active:scale-100 \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent-soft data-[state=checked]:shadow-glow\",\n // The card's check: an empty ring at rest, the accent filled with its check once chosen.\n cardCheck:\n \"absolute right-3.5 top-3.5 inline-flex h-5 w-5 items-center justify-center rounded-full border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\n \"group-data-[state=checked]:border-accent group-data-[state=checked]:bg-accent\",\n // The tactile key: on a ledge (its lower edge), its hairline stronger on hover, pressed in while held and once chosen.\n button:\n \"relative inline-flex min-h-12 min-w-12 select-none flex-col items-center justify-center gap-0.5 rounded-xl border border-border-default bg-surface-2 bg-sheen px-4 py-2 \" +\n \"text-sm font-semibold text-fg inset-shadow-edge shadow-[0_4px_0_0_var(--kz-border-strong)] \" +\n \"transition-[transform,box-shadow,background-color,border-color,color] duration-100 ease-out motion-reduce:transition-none \" +\n \"hover:border-border-strong \" +\n \"active:translate-y-[3px] active:shadow-[0_1px_0_0_var(--kz-border-strong)] \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:translate-y-[3px] data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:bg-fill-shade \" +\n \"data-[state=checked]:text-on-accent data-[state=checked]:inset-shadow-edge-fill data-[state=checked]:shadow-[0_1px_0_0_var(--kz-accent-hover)]\",\n buttonHint: \"text-[11px] font-medium text-fg-secondary group-data-[state=checked]:text-on-accent\",\n segmented:\n \"relative isolate inline-grid auto-cols-fr grid-flow-col gap-1 rounded-xl border border-border-default bg-surface-2 p-1 inset-shadow-well \" +\n \"before:pointer-events-none before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) \" +\n \"before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:border before:border-border-default before:bg-surface-0 before:bg-sheen \" +\n \"before:opacity-0 before:shadow-raised before:transition-[translate,width,height,opacity] before:duration-300 motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n segment:\n \"inline-flex min-h-9 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-lg px-3.5 text-sm font-semibold text-fg-secondary transition-colors \" +\n \"hover:text-fg focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border data-[state=checked]:border-border-default data-[state=checked]:bg-surface-0 data-[state=checked]:text-fg data-[state=checked]:shadow-raised \" +\n \"group-data-[indicator=on]/seg:data-[state=checked]:border-transparent group-data-[indicator=on]/seg:data-[state=checked]:bg-transparent group-data-[indicator=on]/seg:data-[state=checked]:shadow-none\",\n },\n variants: {\n variant: {\n list: {},\n cards: { root: \"grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3\" },\n buttons: { root: \"flex flex-wrap gap-2.5 pb-1\" },\n segmented: {},\n },\n },\n defaultVariants: { variant: \"list\" },\n});\nconst s = radioGroup();\n\nexport type RadioGroupRootProps = React.ComponentProps<typeof RadioGroupPrimitive.Root> & {\n /** The group's accessible name — passed translated (or `aria-labelledby` a visible heading). */\n label?: string;\n /** Marks every radio invalid: aria-invalid (pair it with a `Field.Error`). */\n invalid?: boolean;\n /**\n * The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or\n * `segmented` (a track of `Segment`s, one light gliding to the choice).\n * @default \"list\"\n */\n variant?: \"list\" | \"cards\" | \"buttons\" | \"segmented\";\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout. */\nexport function RadioGroupRoot({ label, invalid, variant = \"list\", className, ...props }: RadioGroupRootProps) {\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=radio]\", active: \"[role=radio][data-state=checked]\" });\n const segmented = variant === \"segmented\";\n return (\n <RadioGroupPrimitive.Root\n ref={segmented ? ref : undefined}\n aria-label={label}\n aria-invalid={invalid || undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-variant={variant}\n orientation={segmented ? \"horizontal\" : props.orientation}\n className={segmented ? s.segmented({ className: [\"group/seg\", className].filter(Boolean).join(\" \") }) : radioGroup({ variant }).root({ className })}\n {...props}\n />\n );\n}\n\nexport type RadioGroupItemProps = Omit<React.ComponentProps<typeof RadioGroupPrimitive.Item>, \"children\"> & {\n /** The words next to the dot, clickable. Otherwise name it with `Field.Label htmlFor` or `aria-label`. */\n children?: React.ReactNode;\n};\n\n/** A radio: a dot, and its words when given. */\nexport function RadioGroupItem({ className, children, ...props }: RadioGroupItemProps) {\n const item = (\n <RadioGroupPrimitive.Item className={s.item({ className: children ? \"mt-0.5\" : className })} {...props}>\n <RadioGroupPrimitive.Indicator className={s.dot()} />\n </RadioGroupPrimitive.Item>\n );\n if (!children) return item;\n return (\n <label className={s.label({ className })}>\n {item}\n <span className=\"min-w-0\">{children}</span>\n </label>\n );\n}\n\n/** A whole card as the radio: its content is the radio's name; its check lights up when chosen. */\nexport function RadioGroupCard({ className, children, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item className={s.card({ className })} {...props}>\n {children}\n <span aria-hidden className={s.cardCheck()}>\n <RadioGroupPrimitive.Indicator forceMount className=\"inline-flex data-[state=unchecked]:hidden\">\n <CheckGlyph className=\"h-3 w-3\" />\n </RadioGroupPrimitive.Indicator>\n </span>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport type RadioGroupButtonProps = React.ComponentProps<typeof RadioGroupPrimitive.Item> & {\n /** A second line under the words (\"+15 %\"), part of the radio's name. */\n hint?: React.ReactNode;\n};\n\n/** A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level. */\nexport function RadioGroupButton({ className, children, hint, ...props }: RadioGroupButtonProps) {\n return (\n <RadioGroupPrimitive.Item className={s.button({ className: [\"group\", className].filter(Boolean).join(\" \") })} {...props}>\n <span>{children}</span>\n {hint ? <span className={s.buttonHint()}>{hint}</span> : null}\n </RadioGroupPrimitive.Item>\n );\n}\n\n/** A segment of a `variant=\"segmented\"` group: equal widths, one light gliding to the choice. */\nexport function RadioGroupSegment({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return <RadioGroupPrimitive.Item className={s.segment({ className })} {...props} />;\n}\n\n/** `RadioGroup.Root/Item/Card/Button/Segment`. */\nexport const RadioGroup = { Root: RadioGroupRoot, Item: RadioGroupItem, Card: RadioGroupCard, Button: RadioGroupButton, Segment: RadioGroupSegment };\n"
|
|
335
335
|
},
|
|
336
336
|
{
|
|
337
337
|
"path": "sliding-indicator.ts",
|