create-tanqory-theme 0.0.0-stage → 0.2.2
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/LICENSE +21 -0
- package/README.md +55 -2
- package/bin/create-tanqory-theme.mjs +13 -0
- package/lib/agents.mjs +118 -0
- package/lib/create.mjs +245 -0
- package/package.json +39 -4
- package/templates/blank/README.md +13 -0
- package/templates/blank/config/settings.json +3 -0
- package/templates/blank/locales/en.json +22 -0
- package/templates/blank/locales/th.json +22 -0
- package/templates/blank/package.json +34 -0
- package/templates/blank/regions/footer.json +35 -0
- package/templates/blank/regions/header.json +13 -0
- package/templates/blank/scripts/build.mjs +193 -0
- package/templates/blank/scripts/probe.ts +4 -0
- package/templates/blank/src/bundle.ts +15 -0
- package/templates/blank/src/components/parts.tsx +32 -0
- package/templates/blank/src/entry.client.tsx +6 -0
- package/templates/blank/src/entry.server.tsx +13 -0
- package/templates/blank/src/sections/chrome.tsx +132 -0
- package/templates/blank/src/sections/commerce.tsx +242 -0
- package/templates/blank/src/sections/content.tsx +111 -0
- package/templates/blank/src/sections/editorial.tsx +107 -0
- package/templates/blank/src/settings.ts +15 -0
- package/templates/blank/src/styles/theme.css +195 -0
- package/templates/blank/src/templates.ts +14 -0
- package/templates/blank/src/theme.tsx +76 -0
- package/templates/blank/template.json +24 -0
- package/templates/blank/templates/404.json +14 -0
- package/templates/blank/templates/account.json +14 -0
- package/templates/blank/templates/article.json +14 -0
- package/templates/blank/templates/blog.json +14 -0
- package/templates/blank/templates/cart.json +14 -0
- package/templates/blank/templates/collection.json +14 -0
- package/templates/blank/templates/index.json +36 -0
- package/templates/blank/templates/page.contact.json +18 -0
- package/templates/blank/templates/page.json +14 -0
- package/templates/blank/templates/product.json +18 -0
- package/templates/blank/templates/search.json +14 -0
- package/templates/blank/tests/theme.test.ts +49 -0
- package/templates/blank/tsconfig.json +17 -0
- package/templates/blank/vite.client.config.ts +17 -0
- package/templates/blank/vite.probe.config.ts +16 -0
- package/templates/blank/vite.server.config.ts +32 -0
- package/templates/blank/vitest.config.ts +9 -0
- package/templates/common/.claude/settings.json +8 -0
- package/templates/common/.codex/config.toml +4 -0
- package/templates/common/.gemini/settings.json +12 -0
- package/templates/common/.github/workflows/theme-check.yml +44 -0
- package/templates/common/.mcp.json +12 -0
- package/templates/common/.vscode/mcp.json +9 -0
- package/templates/common/_gitignore +9 -0
- package/templates/common/scripts/check.mjs +44 -0
- package/templates/common/scripts/dev.mjs +30 -0
- package/templates/common/tanqory.config.json +6 -0
- package/templates/nova/.gitattributes +3 -0
- package/templates/nova/.tanqory/contracts/LOCK.json +66 -0
- package/templates/nova/.tanqory/contracts/nova-01.json +1092 -0
- package/templates/nova/.tanqory/contracts/nova-12A.json +2674 -0
- package/templates/nova/.tanqory/contracts/nova-12B.json +2285 -0
- package/templates/nova/.tanqory/contracts/nova-12C.json +2848 -0
- package/templates/nova/.tanqory/contracts/nova-12D.json +3039 -0
- package/templates/nova/.tanqory/contracts/nova-12E.json +1473 -0
- package/templates/nova/.tanqory/contracts/nova-12F.json +1670 -0
- package/templates/nova/.tanqory/contracts/nova-12G.json +1661 -0
- package/templates/nova/.tanqory/contracts/nova-13.json +301 -0
- package/templates/nova/.tanqory/contracts/nova-14.json +172 -0
- package/templates/nova/.tanqory/legacy-settings.json +1121 -0
- package/templates/nova/README.md +150 -0
- package/templates/nova/_gitignore +28 -0
- package/templates/nova/assets/ai-tokens.css +6 -0
- package/templates/nova/assets/starter/starter-art-1200x800-0b773ac4.svg +1 -0
- package/templates/nova/assets/starter/starter-art-1200x900-59600290.svg +1 -0
- package/templates/nova/assets/starter/starter-art-1200x900-c549d496.svg +1 -0
- package/templates/nova/assets/starter/starter-art-1600x900-8f979fcb.svg +1 -0
- package/templates/nova/assets/starter/starter-art-2100x900-cbb71bfd.svg +1 -0
- package/templates/nova/config/legacy-settings.json +438 -0
- package/templates/nova/config/promotion-popup.schema.ts +29 -0
- package/templates/nova/config/settings.json +23 -0
- package/templates/nova/config/settings.schema.ts +231 -0
- package/templates/nova/entry.abi.tsx +27 -0
- package/templates/nova/groups/footer.json +39 -0
- package/templates/nova/groups/header.json +11 -0
- package/templates/nova/lib/abi/abi.css +41 -0
- package/templates/nova/lib/bundle-core.ts +35 -0
- package/templates/nova/lib/bundle.client.ts +15 -0
- package/templates/nova/lib/bundle.ts +15 -0
- package/templates/nova/lib/legacy-settings.ts +104 -0
- package/templates/nova/locales/ar.json +590 -0
- package/templates/nova/locales/en.json +590 -0
- package/templates/nova/locales/th.json +590 -0
- package/templates/nova/main.abi.tsx +12 -0
- package/templates/nova/package.json +44 -0
- package/templates/nova/scripts/abi/build.mjs +145 -0
- package/templates/nova/scripts/abi/check-content.mjs +99 -0
- package/templates/nova/scripts/abi/jcs.mjs +27 -0
- package/templates/nova/scripts/abi/local-host.mjs +294 -0
- package/templates/nova/scripts/abi/manifest.mjs +392 -0
- package/templates/nova/scripts/abi/starter-assets.mjs +32 -0
- package/templates/nova/scripts/compiler/checks/check-a11y.mjs +88 -0
- package/templates/nova/scripts/compiler/checks/check-layout-sanity.mjs +169 -0
- package/templates/nova/scripts/compiler/checks/check-page-headings.mjs +67 -0
- package/templates/nova/scripts/compiler/checks/check-product-jsonld.mjs +80 -0
- package/templates/nova/scripts/compiler/checks/theme-spec/check.mjs +88 -0
- package/templates/nova/scripts/compiler/contracts/extract.mjs +188 -0
- package/templates/nova/scripts/compiler/contracts/index.ts +125 -0
- package/templates/nova/scripts/compiler/contracts/validate.ts +347 -0
- package/templates/nova/scripts/compiler/manifest/gen-manifest.mjs +156 -0
- package/templates/nova/scripts/compiler/manifest/manifest-entry.ts +386 -0
- package/templates/nova/scripts/compiler/manifest/manifest-sections.ts +247 -0
- package/templates/nova/scripts/compiler/migrate/migrate-content.mjs +171 -0
- package/templates/nova/scripts/compiler/migrate/migrate-groups.mjs +159 -0
- package/templates/nova/scripts/parity/case-shim.ts +14 -0
- package/templates/nova/scripts/parity/emit.ts +206 -0
- package/templates/nova/scripts/parity/index.html +12 -0
- package/templates/nova/scripts/parity/main.tsx +23 -0
- package/templates/nova/scripts/parity/pages.tsx +123 -0
- package/templates/nova/scripts/parity/vite.config.ts +29 -0
- package/templates/nova/src/components/base/Accordion.css +20 -0
- package/templates/nova/src/components/base/Accordion.tsx +50 -0
- package/templates/nova/src/components/base/Badge.css +17 -0
- package/templates/nova/src/components/base/Badge.tsx +26 -0
- package/templates/nova/src/components/base/Button.css +73 -0
- package/templates/nova/src/components/base/Button.tsx +64 -0
- package/templates/nova/src/components/base/Checkbox.tsx +18 -0
- package/templates/nova/src/components/base/Chip.css +20 -0
- package/templates/nova/src/components/base/Chip.tsx +33 -0
- package/templates/nova/src/components/base/Divider.css +6 -0
- package/templates/nova/src/components/base/Divider.tsx +28 -0
- package/templates/nova/src/components/base/IconButton.css +34 -0
- package/templates/nova/src/components/base/IconButton.tsx +41 -0
- package/templates/nova/src/components/base/Input.css +21 -0
- package/templates/nova/src/components/base/Input.tsx +24 -0
- package/templates/nova/src/components/base/Link.css +15 -0
- package/templates/nova/src/components/base/Link.tsx +23 -0
- package/templates/nova/src/components/base/Radio.tsx +13 -0
- package/templates/nova/src/components/base/Select.css +18 -0
- package/templates/nova/src/components/base/Select.tsx +34 -0
- package/templates/nova/src/components/base/Skeleton.css +17 -0
- package/templates/nova/src/components/base/Skeleton.tsx +23 -0
- package/templates/nova/src/components/base/Spinner.css +13 -0
- package/templates/nova/src/components/base/Spinner.tsx +8 -0
- package/templates/nova/src/components/base/Switch.tsx +16 -0
- package/templates/nova/src/components/base/Tabs.css +24 -0
- package/templates/nova/src/components/base/Tabs.tsx +61 -0
- package/templates/nova/src/components/base/Textarea.css +1 -0
- package/templates/nova/src/components/base/Textarea.tsx +8 -0
- package/templates/nova/src/components/base/choice-props.ts +9 -0
- package/templates/nova/src/components/base/choice.css +69 -0
- package/templates/nova/src/components/commerce/AddToCart.css +4 -0
- package/templates/nova/src/components/commerce/AddToCart.tsx +77 -0
- package/templates/nova/src/components/commerce/CartLine.css +26 -0
- package/templates/nova/src/components/commerce/CartLine.tsx +82 -0
- package/templates/nova/src/components/commerce/CartSummary.css +23 -0
- package/templates/nova/src/components/commerce/CartSummary.tsx +120 -0
- package/templates/nova/src/components/commerce/CollectionCard.css +66 -0
- package/templates/nova/src/components/commerce/CollectionCard.tsx +57 -0
- package/templates/nova/src/components/commerce/ComparisonTable.css +33 -0
- package/templates/nova/src/components/commerce/ComparisonTable.tsx +113 -0
- package/templates/nova/src/components/commerce/EarnHint.css +3 -0
- package/templates/nova/src/components/commerce/EarnHint.tsx +31 -0
- package/templates/nova/src/components/commerce/Hotspot.css +16 -0
- package/templates/nova/src/components/commerce/Hotspot.tsx +64 -0
- package/templates/nova/src/components/commerce/ListingParts.css +79 -0
- package/templates/nova/src/components/commerce/ListingParts.tsx +211 -0
- package/templates/nova/src/components/commerce/LookItem.css +19 -0
- package/templates/nova/src/components/commerce/LookItem.tsx +41 -0
- package/templates/nova/src/components/commerce/MiniCartLine.css +20 -0
- package/templates/nova/src/components/commerce/MiniCartLine.tsx +93 -0
- package/templates/nova/src/components/commerce/PaymentMark.css +2 -0
- package/templates/nova/src/components/commerce/PaymentMark.tsx +55 -0
- package/templates/nova/src/components/commerce/Price.css +27 -0
- package/templates/nova/src/components/commerce/Price.tsx +55 -0
- package/templates/nova/src/components/commerce/ProductCard.css +66 -0
- package/templates/nova/src/components/commerce/ProductCard.tsx +117 -0
- package/templates/nova/src/components/commerce/ProductGallery.css +72 -0
- package/templates/nova/src/components/commerce/ProductGallery.tsx +98 -0
- package/templates/nova/src/components/commerce/QuantitySelector.css +23 -0
- package/templates/nova/src/components/commerce/QuantitySelector.tsx +45 -0
- package/templates/nova/src/components/commerce/Reviews.css +34 -0
- package/templates/nova/src/components/commerce/Reviews.tsx +112 -0
- package/templates/nova/src/components/commerce/Stars.css +7 -0
- package/templates/nova/src/components/commerce/Stars.tsx +38 -0
- package/templates/nova/src/components/commerce/VariantPicker.css +31 -0
- package/templates/nova/src/components/commerce/VariantPicker.tsx +122 -0
- package/templates/nova/src/components/commerce/blocks/Badge.css +6 -0
- package/templates/nova/src/components/commerce/blocks/Badge.tsx +33 -0
- package/templates/nova/src/components/commerce/blocks/PaymentIcons.tsx +43 -0
- package/templates/nova/src/components/commerce/blocks/Swatches.tsx +17 -0
- package/templates/nova/src/components/commerce/blocks/payment.css +2 -0
- package/templates/nova/src/components/commerce/cart-limit.ts +11 -0
- package/templates/nova/src/components/commerce/money.ts +34 -0
- package/templates/nova/src/components/commerce/swatch.ts +54 -0
- package/templates/nova/src/components/content/ArticleCard.css +22 -0
- package/templates/nova/src/components/content/ArticleCard.tsx +53 -0
- package/templates/nova/src/components/content/BeforeAfterSlider.css +38 -0
- package/templates/nova/src/components/content/BeforeAfterSlider.tsx +87 -0
- package/templates/nova/src/components/content/CardRow.css +80 -0
- package/templates/nova/src/components/content/CardRow.tsx +140 -0
- package/templates/nova/src/components/content/CarouselControls.css +61 -0
- package/templates/nova/src/components/content/CarouselControls.tsx +90 -0
- package/templates/nova/src/components/content/ContentsNav.css +16 -0
- package/templates/nova/src/components/content/ContentsNav.tsx +55 -0
- package/templates/nova/src/components/content/Countdown.css +24 -0
- package/templates/nova/src/components/content/Countdown.tsx +115 -0
- package/templates/nova/src/components/content/HeroCopy.css +92 -0
- package/templates/nova/src/components/content/HeroCopy.tsx +148 -0
- package/templates/nova/src/components/content/HeroFrame.css +75 -0
- package/templates/nova/src/components/content/HeroFrame.tsx +49 -0
- package/templates/nova/src/components/content/HeroMedia.css +40 -0
- package/templates/nova/src/components/content/HeroMedia.tsx +170 -0
- package/templates/nova/src/components/content/LongForm.css +40 -0
- package/templates/nova/src/components/content/LongForm.tsx +56 -0
- package/templates/nova/src/components/content/MediaFrame.css +5 -0
- package/templates/nova/src/components/content/MediaFrame.tsx +52 -0
- package/templates/nova/src/components/content/RichText.tsx +30 -0
- package/templates/nova/src/components/content/SectionHead.css +8 -0
- package/templates/nova/src/components/content/SectionHead.tsx +34 -0
- package/templates/nova/src/components/content/ShareButtons.css +2 -0
- package/templates/nova/src/components/content/ShareButtons.tsx +40 -0
- package/templates/nova/src/components/content/StoreCard.css +14 -0
- package/templates/nova/src/components/content/StoreCard.tsx +60 -0
- package/templates/nova/src/components/content/TestimonialCard.css +36 -0
- package/templates/nova/src/components/content/TestimonialCard.tsx +102 -0
- package/templates/nova/src/components/content/UgcTile.css +8 -0
- package/templates/nova/src/components/content/UgcTile.tsx +33 -0
- package/templates/nova/src/components/content/blocks/Button.tsx +36 -0
- package/templates/nova/src/components/content/blocks/Column.tsx +30 -0
- package/templates/nova/src/components/content/blocks/Heading.tsx +31 -0
- package/templates/nova/src/components/content/blocks/Icon.tsx +25 -0
- package/templates/nova/src/components/content/blocks/Image.tsx +42 -0
- package/templates/nova/src/components/content/blocks/Text.tsx +26 -0
- package/templates/nova/src/components/content/blocks/Video.tsx +44 -0
- package/templates/nova/src/components/content/blocks/content.css +25 -0
- package/templates/nova/src/components/content/blocks/shared.ts +5 -0
- package/templates/nova/src/components/forms/ContactForm.css +19 -0
- package/templates/nova/src/components/forms/ContactForm.tsx +139 -0
- package/templates/nova/src/components/forms/FormField.css +15 -0
- package/templates/nova/src/components/forms/FormField.tsx +77 -0
- package/templates/nova/src/components/forms/GatewayFields.tsx +79 -0
- package/templates/nova/src/components/forms/NewsletterForm.css +9 -0
- package/templates/nova/src/components/forms/NewsletterForm.tsx +141 -0
- package/templates/nova/src/components/forms/SearchField.css +7 -0
- package/templates/nova/src/components/forms/SearchField.tsx +68 -0
- package/templates/nova/src/components/forms/blocks/Newsletter.css +5 -0
- package/templates/nova/src/components/forms/blocks/Newsletter.tsx +71 -0
- package/templates/nova/src/components/forms/validate.ts +4 -0
- package/templates/nova/src/components/listing/ProductListing.tsx +200 -0
- package/templates/nova/src/components/navigation/BrandLogo.tsx +35 -0
- package/templates/nova/src/components/navigation/Breadcrumb.css +16 -0
- package/templates/nova/src/components/navigation/Breadcrumb.tsx +59 -0
- package/templates/nova/src/components/navigation/LineAddFriend.css +5 -0
- package/templates/nova/src/components/navigation/LineAddFriend.tsx +42 -0
- package/templates/nova/src/components/navigation/LocaleSelector.css +33 -0
- package/templates/nova/src/components/navigation/LocaleSelector.tsx +64 -0
- package/templates/nova/src/components/navigation/Menu.tsx +26 -0
- package/templates/nova/src/components/navigation/Pagination.css +24 -0
- package/templates/nova/src/components/navigation/Pagination.tsx +74 -0
- package/templates/nova/src/components/navigation/RecoveryLinks.css +8 -0
- package/templates/nova/src/components/navigation/RecoveryLinks.tsx +23 -0
- package/templates/nova/src/components/navigation/SocialLinks.css +5 -0
- package/templates/nova/src/components/navigation/SocialLinks.tsx +51 -0
- package/templates/nova/src/components/navigation/blocks/SocialLinks.tsx +23 -0
- package/templates/nova/src/components/overlays/AccountMenu.css +11 -0
- package/templates/nova/src/components/overlays/AccountMenu.tsx +91 -0
- package/templates/nova/src/components/overlays/CartDrawer.css +17 -0
- package/templates/nova/src/components/overlays/CartDrawer.tsx +118 -0
- package/templates/nova/src/components/overlays/ConfirmationDialog.tsx +49 -0
- package/templates/nova/src/components/overlays/CookieConsent.css +56 -0
- package/templates/nova/src/components/overlays/CookieConsent.tsx +170 -0
- package/templates/nova/src/components/overlays/Drawer.css +38 -0
- package/templates/nova/src/components/overlays/Drawer.tsx +66 -0
- package/templates/nova/src/components/overlays/FriendOfferPanel.tsx +79 -0
- package/templates/nova/src/components/overlays/MarketSelector.css +111 -0
- package/templates/nova/src/components/overlays/MarketSelector.tsx +450 -0
- package/templates/nova/src/components/overlays/Menu.css +9 -0
- package/templates/nova/src/components/overlays/Menu.tsx +124 -0
- package/templates/nova/src/components/overlays/MobileNavDrawer.css +28 -0
- package/templates/nova/src/components/overlays/MobileNavDrawer.tsx +166 -0
- package/templates/nova/src/components/overlays/Modal.css +52 -0
- package/templates/nova/src/components/overlays/Modal.tsx +88 -0
- package/templates/nova/src/components/overlays/Popover.css +4 -0
- package/templates/nova/src/components/overlays/Popover.tsx +115 -0
- package/templates/nova/src/components/overlays/ProductLightbox.css +20 -0
- package/templates/nova/src/components/overlays/ProductLightbox.tsx +70 -0
- package/templates/nova/src/components/overlays/PromoPopup.css +32 -0
- package/templates/nova/src/components/overlays/PromoPopup.tsx +198 -0
- package/templates/nova/src/components/overlays/QuickView.css +31 -0
- package/templates/nova/src/components/overlays/QuickView.tsx +127 -0
- package/templates/nova/src/components/overlays/ReferralPanel.css +16 -0
- package/templates/nova/src/components/overlays/ReferralPanel.tsx +138 -0
- package/templates/nova/src/components/overlays/RewardsLauncher.css +22 -0
- package/templates/nova/src/components/overlays/RewardsLauncher.tsx +270 -0
- package/templates/nova/src/components/overlays/SearchModal.css +48 -0
- package/templates/nova/src/components/overlays/SearchModal.tsx +302 -0
- package/templates/nova/src/components/overlays/Sheet.css +16 -0
- package/templates/nova/src/components/overlays/Sheet.tsx +48 -0
- package/templates/nova/src/components/overlays/SizeGuide.css +8 -0
- package/templates/nova/src/components/overlays/SizeGuide.tsx +74 -0
- package/templates/nova/src/components/overlays/Toast.css +20 -0
- package/templates/nova/src/components/overlays/Toast.tsx +164 -0
- package/templates/nova/src/components/overlays/Tooltip.css +6 -0
- package/templates/nova/src/components/overlays/Tooltip.tsx +59 -0
- package/templates/nova/src/components/overlays/use-dialog.ts +74 -0
- package/templates/nova/src/components/section/shared.css +42 -0
- package/templates/nova/src/components/section/shared.ts +120 -0
- package/templates/nova/src/components/section/shell.tsx +29 -0
- package/templates/nova/src/components/states/Alert.css +10 -0
- package/templates/nova/src/components/states/Alert.tsx +50 -0
- package/templates/nova/src/components/states/EditorFrame.css +31 -0
- package/templates/nova/src/components/states/EditorFrame.tsx +43 -0
- package/templates/nova/src/components/states/MediaState.css +1 -0
- package/templates/nova/src/components/states/MediaState.tsx +49 -0
- package/templates/nova/src/components/states/OfflineNotice.css +4 -0
- package/templates/nova/src/components/states/OfflineNotice.tsx +24 -0
- package/templates/nova/src/components/states/SkeletonLayout.css +22 -0
- package/templates/nova/src/components/states/SkeletonLayout.tsx +104 -0
- package/templates/nova/src/components/states/StateBlock.css +22 -0
- package/templates/nova/src/components/states/StateBlock.tsx +39 -0
- package/templates/nova/src/components/states/media/MediaError.css +6 -0
- package/templates/nova/src/components/states/media/MediaError.tsx +49 -0
- package/templates/nova/src/components/states/media/MediaPlaceholder.css +24 -0
- package/templates/nova/src/components/states/media/MediaPlaceholder.tsx +101 -0
- package/templates/nova/src/components/states/media/placeholder-art.ts +142 -0
- package/templates/nova/src/layouts/Main.tsx +80 -0
- package/templates/nova/src/sections/Account/Account.tsx +109 -0
- package/templates/nova/src/sections/Account/index.ts +22 -0
- package/templates/nova/src/sections/Account/presets.ts +6 -0
- package/templates/nova/src/sections/Account/schema.ts +29 -0
- package/templates/nova/src/sections/AnnouncementBar/AnnouncementBar.css +37 -0
- package/templates/nova/src/sections/AnnouncementBar/AnnouncementBar.tsx +151 -0
- package/templates/nova/src/sections/AnnouncementBar/index.ts +20 -0
- package/templates/nova/src/sections/AnnouncementBar/presets.ts +10 -0
- package/templates/nova/src/sections/AnnouncementBar/schema.ts +26 -0
- package/templates/nova/src/sections/ArticleBody/ArticleBody.tsx +129 -0
- package/templates/nova/src/sections/ArticleBody/index.ts +21 -0
- package/templates/nova/src/sections/ArticleBody/presets.ts +10 -0
- package/templates/nova/src/sections/ArticleBody/schema.ts +40 -0
- package/templates/nova/src/sections/BeforeAfter/BeforeAfter.tsx +47 -0
- package/templates/nova/src/sections/BeforeAfter/blocks/Comparison.tsx +20 -0
- package/templates/nova/src/sections/BeforeAfter/index.ts +28 -0
- package/templates/nova/src/sections/BeforeAfter/presets.ts +10 -0
- package/templates/nova/src/sections/BeforeAfter/schema.ts +34 -0
- package/templates/nova/src/sections/BlogPosts/BlogPosts.tsx +106 -0
- package/templates/nova/src/sections/BlogPosts/index.ts +30 -0
- package/templates/nova/src/sections/BlogPosts/presets.ts +10 -0
- package/templates/nova/src/sections/BlogPosts/schema.ts +42 -0
- package/templates/nova/src/sections/Breadcrumbs/Breadcrumbs.css +10 -0
- package/templates/nova/src/sections/Breadcrumbs/Breadcrumbs.tsx +41 -0
- package/templates/nova/src/sections/Breadcrumbs/index.ts +20 -0
- package/templates/nova/src/sections/Breadcrumbs/presets.ts +6 -0
- package/templates/nova/src/sections/Breadcrumbs/schema.ts +24 -0
- package/templates/nova/src/sections/CartItems/CartItems.tsx +170 -0
- package/templates/nova/src/sections/CartItems/blocks/CartBadge.tsx +14 -0
- package/templates/nova/src/sections/CartItems/index.ts +37 -0
- package/templates/nova/src/sections/CartItems/presets.ts +8 -0
- package/templates/nova/src/sections/CartItems/schema.ts +34 -0
- package/templates/nova/src/sections/CollectionBanner/CollectionBanner.tsx +120 -0
- package/templates/nova/src/sections/CollectionBanner/index.ts +25 -0
- package/templates/nova/src/sections/CollectionBanner/presets.ts +10 -0
- package/templates/nova/src/sections/CollectionBanner/schema.ts +37 -0
- package/templates/nova/src/sections/CollectionLinks/CollectionLinks.tsx +47 -0
- package/templates/nova/src/sections/CollectionLinks/index.ts +20 -0
- package/templates/nova/src/sections/CollectionLinks/presets.ts +8 -0
- package/templates/nova/src/sections/CollectionLinks/schema.ts +33 -0
- package/templates/nova/src/sections/CollectionList/CollectionList.tsx +89 -0
- package/templates/nova/src/sections/CollectionList/blocks/CollectionItem.tsx +25 -0
- package/templates/nova/src/sections/CollectionList/index.ts +33 -0
- package/templates/nova/src/sections/CollectionList/presets.ts +10 -0
- package/templates/nova/src/sections/CollectionList/schema.ts +41 -0
- package/templates/nova/src/sections/ComparisonTable/ComparisonTable.tsx +52 -0
- package/templates/nova/src/sections/ComparisonTable/blocks/CompareItem.tsx +26 -0
- package/templates/nova/src/sections/ComparisonTable/index.ts +31 -0
- package/templates/nova/src/sections/ComparisonTable/presets.ts +8 -0
- package/templates/nova/src/sections/ComparisonTable/schema.ts +31 -0
- package/templates/nova/src/sections/ContactForm/ContactForm.tsx +83 -0
- package/templates/nova/src/sections/ContactForm/blocks/ContactDetail.tsx +13 -0
- package/templates/nova/src/sections/ContactForm/index.ts +29 -0
- package/templates/nova/src/sections/ContactForm/presets.ts +8 -0
- package/templates/nova/src/sections/ContactForm/schema.ts +34 -0
- package/templates/nova/src/sections/Divider/Divider.css +17 -0
- package/templates/nova/src/sections/Divider/Divider.tsx +21 -0
- package/templates/nova/src/sections/Divider/index.ts +20 -0
- package/templates/nova/src/sections/Divider/presets.ts +10 -0
- package/templates/nova/src/sections/Divider/schema.ts +24 -0
- package/templates/nova/src/sections/Faq/Faq.tsx +51 -0
- package/templates/nova/src/sections/Faq/blocks/FaqItem.tsx +13 -0
- package/templates/nova/src/sections/Faq/index.ts +28 -0
- package/templates/nova/src/sections/Faq/presets.ts +8 -0
- package/templates/nova/src/sections/Faq/schema.ts +32 -0
- package/templates/nova/src/sections/FeatureGridBlocks/FeatureGridBlocks.tsx +90 -0
- package/templates/nova/src/sections/FeatureGridBlocks/index.ts +35 -0
- package/templates/nova/src/sections/FeatureGridBlocks/presets.ts +8 -0
- package/templates/nova/src/sections/FeatureGridBlocks/schema.ts +35 -0
- package/templates/nova/src/sections/FeatureHighlights/FeatureHighlights.tsx +56 -0
- package/templates/nova/src/sections/FeatureHighlights/index.ts +20 -0
- package/templates/nova/src/sections/FeatureHighlights/presets.ts +6 -0
- package/templates/nova/src/sections/FeatureHighlights/schema.ts +27 -0
- package/templates/nova/src/sections/FeaturedCollection/FeaturedCollection.tsx +67 -0
- package/templates/nova/src/sections/FeaturedCollection/index.ts +21 -0
- package/templates/nova/src/sections/FeaturedCollection/presets.ts +10 -0
- package/templates/nova/src/sections/FeaturedCollection/schema.ts +39 -0
- package/templates/nova/src/sections/FeaturedProduct/FeaturedProduct.tsx +107 -0
- package/templates/nova/src/sections/FeaturedProduct/index.ts +23 -0
- package/templates/nova/src/sections/FeaturedProduct/presets.ts +8 -0
- package/templates/nova/src/sections/FeaturedProduct/schema.ts +35 -0
- package/templates/nova/src/sections/Footer/Footer.css +81 -0
- package/templates/nova/src/sections/Footer/Footer.tsx +256 -0
- package/templates/nova/src/sections/Footer/blocks/FooterBrand.tsx +40 -0
- package/templates/nova/src/sections/Footer/blocks/FooterLine.tsx +11 -0
- package/templates/nova/src/sections/Footer/blocks/FooterMenu.tsx +14 -0
- package/templates/nova/src/sections/Footer/blocks/FooterText.tsx +8 -0
- package/templates/nova/src/sections/Footer/blocks/footer.css +7 -0
- package/templates/nova/src/sections/Footer/index.ts +38 -0
- package/templates/nova/src/sections/Footer/presets.ts +10 -0
- package/templates/nova/src/sections/Footer/schema.ts +27 -0
- package/templates/nova/src/sections/Group/Group.css +59 -0
- package/templates/nova/src/sections/Group/Group.tsx +167 -0
- package/templates/nova/src/sections/Group/blocks/Accordion.tsx +26 -0
- package/templates/nova/src/sections/Group/blocks/JumboText.tsx +20 -0
- package/templates/nova/src/sections/Group/blocks/Spacer.tsx +16 -0
- package/templates/nova/src/sections/Group/index.ts +36 -0
- package/templates/nova/src/sections/Group/presets.ts +12 -0
- package/templates/nova/src/sections/Group/schema.ts +35 -0
- package/templates/nova/src/sections/Header/Header.css +185 -0
- package/templates/nova/src/sections/Header/Header.tsx +478 -0
- package/templates/nova/src/sections/Header/index.ts +32 -0
- package/templates/nova/src/sections/Header/presets.ts +10 -0
- package/templates/nova/src/sections/Header/schema.ts +32 -0
- package/templates/nova/src/sections/Hero/Hero.tsx +128 -0
- package/templates/nova/src/sections/Hero/index.ts +24 -0
- package/templates/nova/src/sections/Hero/presets.ts +16 -0
- package/templates/nova/src/sections/Hero/schema.ts +56 -0
- package/templates/nova/src/sections/ImageGallery/ImageGallery.tsx +86 -0
- package/templates/nova/src/sections/ImageGallery/index.ts +28 -0
- package/templates/nova/src/sections/ImageGallery/presets.ts +10 -0
- package/templates/nova/src/sections/ImageGallery/schema.ts +34 -0
- package/templates/nova/src/sections/ImageWithText/ImageWithText.tsx +62 -0
- package/templates/nova/src/sections/ImageWithText/index.ts +23 -0
- package/templates/nova/src/sections/ImageWithText/presets.ts +12 -0
- package/templates/nova/src/sections/ImageWithText/schema.ts +45 -0
- package/templates/nova/src/sections/LineAddFriend/LineAddFriend.tsx +25 -0
- package/templates/nova/src/sections/LineAddFriend/index.ts +20 -0
- package/templates/nova/src/sections/LineAddFriend/presets.ts +3 -0
- package/templates/nova/src/sections/LineAddFriend/schema.ts +25 -0
- package/templates/nova/src/sections/LogoList/LogoList.tsx +40 -0
- package/templates/nova/src/sections/LogoList/blocks/Logo.tsx +18 -0
- package/templates/nova/src/sections/LogoList/index.ts +29 -0
- package/templates/nova/src/sections/LogoList/presets.ts +8 -0
- package/templates/nova/src/sections/LogoList/schema.ts +29 -0
- package/templates/nova/src/sections/MainCollection/MainCollection.tsx +51 -0
- package/templates/nova/src/sections/MainCollection/index.ts +26 -0
- package/templates/nova/src/sections/MainCollection/presets.ts +10 -0
- package/templates/nova/src/sections/MainCollection/schema.ts +29 -0
- package/templates/nova/src/sections/Marquee/Marquee.tsx +114 -0
- package/templates/nova/src/sections/Marquee/index.ts +20 -0
- package/templates/nova/src/sections/Marquee/presets.ts +10 -0
- package/templates/nova/src/sections/Marquee/schema.ts +23 -0
- package/templates/nova/src/sections/Multicolumn/Multicolumn.tsx +56 -0
- package/templates/nova/src/sections/Multicolumn/index.ts +25 -0
- package/templates/nova/src/sections/Multicolumn/presets.ts +8 -0
- package/templates/nova/src/sections/Multicolumn/schema.ts +34 -0
- package/templates/nova/src/sections/Newsletter/Newsletter.tsx +64 -0
- package/templates/nova/src/sections/Newsletter/index.ts +20 -0
- package/templates/nova/src/sections/Newsletter/presets.ts +10 -0
- package/templates/nova/src/sections/Newsletter/schema.ts +36 -0
- package/templates/nova/src/sections/NotFound/NotFound.tsx +41 -0
- package/templates/nova/src/sections/NotFound/index.ts +20 -0
- package/templates/nova/src/sections/NotFound/presets.ts +6 -0
- package/templates/nova/src/sections/NotFound/schema.ts +30 -0
- package/templates/nova/src/sections/PageBody/PageBody.tsx +35 -0
- package/templates/nova/src/sections/PageBody/index.ts +21 -0
- package/templates/nova/src/sections/PageBody/presets.ts +6 -0
- package/templates/nova/src/sections/PageBody/schema.ts +29 -0
- package/templates/nova/src/sections/PolicyPage/PolicyPage.tsx +62 -0
- package/templates/nova/src/sections/PolicyPage/index.ts +23 -0
- package/templates/nova/src/sections/PolicyPage/presets.ts +6 -0
- package/templates/nova/src/sections/PolicyPage/schema.ts +27 -0
- package/templates/nova/src/sections/ProductDetails/ProductDetails.tsx +269 -0
- package/templates/nova/src/sections/ProductDetails/blocks/AddToCartBlock.tsx +15 -0
- package/templates/nova/src/sections/ProductDetails/blocks/ProductDescription.tsx +15 -0
- package/templates/nova/src/sections/ProductDetails/blocks/ProductInventory.tsx +38 -0
- package/templates/nova/src/sections/ProductDetails/blocks/ProductPrice.tsx +19 -0
- package/templates/nova/src/sections/ProductDetails/blocks/ProductSku.tsx +15 -0
- package/templates/nova/src/sections/ProductDetails/blocks/ProductTitle.tsx +17 -0
- package/templates/nova/src/sections/ProductDetails/blocks/Quantity.tsx +14 -0
- package/templates/nova/src/sections/ProductDetails/blocks/Tab.tsx +23 -0
- package/templates/nova/src/sections/ProductDetails/blocks/VariantPickerBlock.tsx +44 -0
- package/templates/nova/src/sections/ProductDetails/blocks/commerce-blocks.css +13 -0
- package/templates/nova/src/sections/ProductDetails/index.ts +44 -0
- package/templates/nova/src/sections/ProductDetails/presets.ts +10 -0
- package/templates/nova/src/sections/ProductDetails/schema.ts +44 -0
- package/templates/nova/src/sections/ProductGrid/ProductGrid.tsx +38 -0
- package/templates/nova/src/sections/ProductGrid/index.ts +25 -0
- package/templates/nova/src/sections/ProductGrid/presets.ts +6 -0
- package/templates/nova/src/sections/ProductGrid/schema.ts +37 -0
- package/templates/nova/src/sections/ProductRecommendations/ProductRecommendations.tsx +45 -0
- package/templates/nova/src/sections/ProductRecommendations/index.ts +20 -0
- package/templates/nova/src/sections/ProductRecommendations/presets.ts +8 -0
- package/templates/nova/src/sections/ProductRecommendations/schema.ts +28 -0
- package/templates/nova/src/sections/ProductReviews/ProductReviews.tsx +136 -0
- package/templates/nova/src/sections/ProductReviews/index.ts +22 -0
- package/templates/nova/src/sections/ProductReviews/presets.ts +6 -0
- package/templates/nova/src/sections/ProductReviews/schema.ts +31 -0
- package/templates/nova/src/sections/PromoBanner/PromoBanner.tsx +78 -0
- package/templates/nova/src/sections/PromoBanner/index.ts +20 -0
- package/templates/nova/src/sections/PromoBanner/presets.ts +10 -0
- package/templates/nova/src/sections/PromoBanner/schema.ts +38 -0
- package/templates/nova/src/sections/RecentlyViewed/RecentlyViewed.tsx +53 -0
- package/templates/nova/src/sections/RecentlyViewed/index.ts +20 -0
- package/templates/nova/src/sections/RecentlyViewed/presets.ts +6 -0
- package/templates/nova/src/sections/RecentlyViewed/schema.ts +26 -0
- package/templates/nova/src/sections/RichText/RichText.tsx +38 -0
- package/templates/nova/src/sections/RichText/index.ts +20 -0
- package/templates/nova/src/sections/RichText/presets.ts +10 -0
- package/templates/nova/src/sections/RichText/schema.ts +37 -0
- package/templates/nova/src/sections/SearchResults/SearchResults.tsx +112 -0
- package/templates/nova/src/sections/SearchResults/index.ts +26 -0
- package/templates/nova/src/sections/SearchResults/presets.ts +8 -0
- package/templates/nova/src/sections/SearchResults/schema.ts +30 -0
- package/templates/nova/src/sections/ShopTheLook/ShopTheLook.tsx +112 -0
- package/templates/nova/src/sections/ShopTheLook/blocks/Hotspot.tsx +22 -0
- package/templates/nova/src/sections/ShopTheLook/index.ts +33 -0
- package/templates/nova/src/sections/ShopTheLook/presets.ts +8 -0
- package/templates/nova/src/sections/ShopTheLook/schema.ts +31 -0
- package/templates/nova/src/sections/Slideshow/Slideshow.tsx +199 -0
- package/templates/nova/src/sections/Slideshow/blocks/Slide.tsx +93 -0
- package/templates/nova/src/sections/Slideshow/index.ts +30 -0
- package/templates/nova/src/sections/Slideshow/presets.ts +10 -0
- package/templates/nova/src/sections/Slideshow/schema.ts +36 -0
- package/templates/nova/src/sections/StoreLocator/StoreLocator.tsx +66 -0
- package/templates/nova/src/sections/StoreLocator/index.ts +20 -0
- package/templates/nova/src/sections/StoreLocator/presets.ts +6 -0
- package/templates/nova/src/sections/StoreLocator/schema.ts +34 -0
- package/templates/nova/src/sections/TeamFounders/TeamFounders.tsx +74 -0
- package/templates/nova/src/sections/TeamFounders/blocks/Person.tsx +28 -0
- package/templates/nova/src/sections/TeamFounders/index.ts +28 -0
- package/templates/nova/src/sections/TeamFounders/presets.ts +10 -0
- package/templates/nova/src/sections/TeamFounders/schema.ts +36 -0
- package/templates/nova/src/sections/Testimonials/Testimonials.tsx +83 -0
- package/templates/nova/src/sections/Testimonials/blocks/Testimonial.tsx +30 -0
- package/templates/nova/src/sections/Testimonials/index.ts +29 -0
- package/templates/nova/src/sections/Testimonials/presets.ts +10 -0
- package/templates/nova/src/sections/Testimonials/schema.ts +35 -0
- package/templates/nova/src/sections/Timeline/Timeline.tsx +43 -0
- package/templates/nova/src/sections/Timeline/blocks/Step.tsx +19 -0
- package/templates/nova/src/sections/Timeline/index.ts +28 -0
- package/templates/nova/src/sections/Timeline/presets.ts +8 -0
- package/templates/nova/src/sections/Timeline/schema.ts +31 -0
- package/templates/nova/src/sections/TrustBadges/TrustBadges.tsx +42 -0
- package/templates/nova/src/sections/TrustBadges/index.ts +29 -0
- package/templates/nova/src/sections/TrustBadges/presets.ts +8 -0
- package/templates/nova/src/sections/TrustBadges/schema.ts +29 -0
- package/templates/nova/src/sections/UgcGallery/UgcGallery.tsx +58 -0
- package/templates/nova/src/sections/UgcGallery/blocks/UgcImage.tsx +27 -0
- package/templates/nova/src/sections/UgcGallery/index.ts +31 -0
- package/templates/nova/src/sections/UgcGallery/presets.ts +8 -0
- package/templates/nova/src/sections/UgcGallery/schema.ts +35 -0
- package/templates/nova/src/sections/VideoHero/VideoHero.tsx +75 -0
- package/templates/nova/src/sections/VideoHero/index.ts +22 -0
- package/templates/nova/src/sections/VideoHero/presets.ts +8 -0
- package/templates/nova/src/sections/VideoHero/schema.ts +43 -0
- package/templates/nova/src/sections/data.ts +115 -0
- package/templates/nova/src/sections/shipped-copy.ts +25 -0
- package/templates/nova/src/sections/starter.ts +49 -0
- package/templates/nova/src/styles/Container.css +6 -0
- package/templates/nova/src/styles/Container.tsx +13 -0
- package/templates/nova/src/styles/Icon.css +2 -0
- package/templates/nova/src/styles/Icon.tsx +101 -0
- package/templates/nova/src/styles/NovaProvider.tsx +91 -0
- package/templates/nova/src/styles/base.css +142 -0
- package/templates/nova/src/styles/blocks.css +6 -0
- package/templates/nova/src/styles/fonts/OFL.txt +93 -0
- package/templates/nova/src/styles/fonts/geist-cyrillic-ext.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-cyrillic.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-latin-ext.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-latin.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-mono-cyrillic-ext.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-mono-cyrillic.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-mono-latin-ext.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-mono-latin.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-mono-vietnamese.woff2 +0 -0
- package/templates/nova/src/styles/fonts/geist-vietnamese.woff2 +0 -0
- package/templates/nova/src/styles/fonts.css +95 -0
- package/templates/nova/src/styles/global-settings.css +88 -0
- package/templates/nova/src/styles/index.css +6 -0
- package/templates/nova/src/styles/motion.ts +53 -0
- package/templates/nova/src/styles/scroll.ts +24 -0
- package/templates/nova/src/styles/sections/commerce.css +147 -0
- package/templates/nova/src/styles/sections/content.css +304 -0
- package/templates/nova/src/styles/sections/editorial.css +131 -0
- package/templates/nova/src/styles/sections/promotion.css +336 -0
- package/templates/nova/src/styles/sections/trust.css +117 -0
- package/templates/nova/src/styles/sections/utility.css +190 -0
- package/templates/nova/src/styles/strings.ts +492 -0
- package/templates/nova/src/styles/text.ts +4 -0
- package/templates/nova/src/styles/theme.ts +171 -0
- package/templates/nova/src/styles/tokens.css +600 -0
- package/templates/nova/src/styles/viewport.ts +23 -0
- package/templates/nova/template.json +94 -0
- package/templates/nova/templates/404.json +30 -0
- package/templates/nova/templates/account.json +15 -0
- package/templates/nova/templates/article.json +41 -0
- package/templates/nova/templates/article.longform.json +17 -0
- package/templates/nova/templates/blog.featured.json +22 -0
- package/templates/nova/templates/blog.json +29 -0
- package/templates/nova/templates/cart.json +67 -0
- package/templates/nova/templates/collection.featured.json +43 -0
- package/templates/nova/templates/collection.json +30 -0
- package/templates/nova/templates/contact.json +65 -0
- package/templates/nova/templates/index.json +167 -0
- package/templates/nova/templates/list-collections.json +19 -0
- package/templates/nova/templates/page.about.json +134 -0
- package/templates/nova/templates/page.contact.json +27 -0
- package/templates/nova/templates/page.json +27 -0
- package/templates/nova/templates/policy.json +23 -0
- package/templates/nova/templates/product.bundle.json +145 -0
- package/templates/nova/templates/product.json +119 -0
- package/templates/nova/templates/search.json +17 -0
- package/templates/nova/tests/abi-ai-contract.test.ts +99 -0
- package/templates/nova/tests/abi-forms.test.tsx +269 -0
- package/templates/nova/tests/abi-inventory.test.tsx +197 -0
- package/templates/nova/tests/abi-legacy-settings.test.tsx +105 -0
- package/templates/nova/tests/abi-opt-in.test.tsx +100 -0
- package/templates/nova/tests/abi-parity.test.tsx +95 -0
- package/templates/nova/tests/abi-r5-store-settings.test.tsx +337 -0
- package/templates/nova/tests/abi-render.test.tsx +178 -0
- package/templates/nova/tests/abi-shop-links.test.tsx +141 -0
- package/templates/nova/tests/abi-storefront-basics.test.tsx +233 -0
- package/templates/nova/tests/abi-variant-url.test.tsx +73 -0
- package/templates/nova/tests/add-refused-no-success.test.tsx +193 -0
- package/templates/nova/tests/architecture.test.ts +63 -0
- package/templates/nova/tests/architecture.ts +160 -0
- package/templates/nova/tests/audit-guards.test.ts +31 -0
- package/templates/nova/tests/bundle-split.test.ts +29 -0
- package/templates/nova/tests/cart-limit-message.test.tsx +212 -0
- package/templates/nova/tests/cart-link-message.test.tsx +98 -0
- package/templates/nova/tests/cases/components/Overview.cases.tsx +8 -0
- package/templates/nova/tests/cases/components/base/Accordion.cases.tsx +40 -0
- package/templates/nova/tests/cases/components/base/Badge.cases.tsx +30 -0
- package/templates/nova/tests/cases/components/base/Button.cases.tsx +73 -0
- package/templates/nova/tests/cases/components/base/Checkbox.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/base/Chip.cases.tsx +37 -0
- package/templates/nova/tests/cases/components/base/Divider.cases.tsx +26 -0
- package/templates/nova/tests/cases/components/base/IconButton.cases.tsx +28 -0
- package/templates/nova/tests/cases/components/base/Input.cases.tsx +28 -0
- package/templates/nova/tests/cases/components/base/Link.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/base/Radio.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/base/Select.cases.tsx +26 -0
- package/templates/nova/tests/cases/components/base/Skeleton.cases.tsx +29 -0
- package/templates/nova/tests/cases/components/base/Spinner.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/base/Switch.cases.tsx +26 -0
- package/templates/nova/tests/cases/components/base/Tabs.cases.tsx +39 -0
- package/templates/nova/tests/cases/components/base/Textarea.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/commerce/AddToCart.cases.tsx +36 -0
- package/templates/nova/tests/cases/components/commerce/CartLine.cases.tsx +20 -0
- package/templates/nova/tests/cases/components/commerce/CartSummary.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/commerce/CollectionCard.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/commerce/ComparisonTable.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/commerce/Hotspot.cases.tsx +25 -0
- package/templates/nova/tests/cases/components/commerce/LookItem.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/commerce/PaymentMark.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/commerce/Price.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/commerce/ProductCard.cases.tsx +67 -0
- package/templates/nova/tests/cases/components/commerce/ProductGallery.cases.tsx +39 -0
- package/templates/nova/tests/cases/components/commerce/QuantitySelector.cases.tsx +31 -0
- package/templates/nova/tests/cases/components/commerce/Reviews.cases.tsx +35 -0
- package/templates/nova/tests/cases/components/commerce/Stars.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/commerce/VariantPicker.cases.tsx +38 -0
- package/templates/nova/tests/cases/components/commerce/blocks/Badge.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/commerce/blocks/PaymentIcons.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/commerce/blocks/Swatches.cases.tsx +22 -0
- package/templates/nova/tests/cases/components/content/ArticleCard.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/BeforeAfterSlider.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/content/CardRow.cases.tsx +23 -0
- package/templates/nova/tests/cases/components/content/CarouselControls.cases.tsx +48 -0
- package/templates/nova/tests/cases/components/content/ContentsNav.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/Countdown.cases.tsx +28 -0
- package/templates/nova/tests/cases/components/content/HeroCopy.cases.tsx +42 -0
- package/templates/nova/tests/cases/components/content/HeroFrame.cases.tsx +22 -0
- package/templates/nova/tests/cases/components/content/HeroMedia.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/content/LongForm.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/MediaFrame.cases.tsx +32 -0
- package/templates/nova/tests/cases/components/content/RichText.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/content/SectionHead.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/content/ShareButtons.cases.tsx +24 -0
- package/templates/nova/tests/cases/components/content/StoreCard.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/TestimonialCard.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/content/UgcTile.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/content/blocks/Button.cases.tsx +20 -0
- package/templates/nova/tests/cases/components/content/blocks/Column.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/blocks/Heading.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/content/blocks/Icon.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/content/blocks/Image.cases.tsx +22 -0
- package/templates/nova/tests/cases/components/content/blocks/Text.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/content/blocks/Video.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/forms/ContactForm.cases.tsx +29 -0
- package/templates/nova/tests/cases/components/forms/FormField.cases.tsx +61 -0
- package/templates/nova/tests/cases/components/forms/NewsletterForm.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/forms/SearchField.cases.tsx +25 -0
- package/templates/nova/tests/cases/components/forms/blocks/Newsletter.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/navigation/BrandLogo.cases.tsx +29 -0
- package/templates/nova/tests/cases/components/navigation/Breadcrumb.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/navigation/LocaleSelector.cases.tsx +32 -0
- package/templates/nova/tests/cases/components/navigation/Menu.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/navigation/Pagination.cases.tsx +25 -0
- package/templates/nova/tests/cases/components/navigation/RecoveryLinks.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/navigation/SocialLinks.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/navigation/blocks/SocialLinks.cases.tsx +17 -0
- package/templates/nova/tests/cases/components/overlays/AccountMenu.cases.tsx +28 -0
- package/templates/nova/tests/cases/components/overlays/CartDrawer.cases.tsx +30 -0
- package/templates/nova/tests/cases/components/overlays/ConfirmationDialog.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/overlays/CookieConsent.cases.tsx +23 -0
- package/templates/nova/tests/cases/components/overlays/CookiePreferences.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/overlays/Drawer.cases.tsx +52 -0
- package/templates/nova/tests/cases/components/overlays/MarketSelector.cases.tsx +82 -0
- package/templates/nova/tests/cases/components/overlays/Menu.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/overlays/MobileNavDrawer.cases.tsx +32 -0
- package/templates/nova/tests/cases/components/overlays/Modal.cases.tsx +71 -0
- package/templates/nova/tests/cases/components/overlays/Popover.cases.tsx +50 -0
- package/templates/nova/tests/cases/components/overlays/PromoPopup.cases.tsx +19 -0
- package/templates/nova/tests/cases/components/overlays/QuickView.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/overlays/SearchModal.cases.tsx +60 -0
- package/templates/nova/tests/cases/components/overlays/Sheet.cases.tsx +32 -0
- package/templates/nova/tests/cases/components/overlays/SizeGuide.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/overlays/Toast.cases.tsx +48 -0
- package/templates/nova/tests/cases/components/overlays/Tooltip.cases.tsx +33 -0
- package/templates/nova/tests/cases/components/states/Alert.cases.tsx +27 -0
- package/templates/nova/tests/cases/components/states/EditorFrame.cases.tsx +36 -0
- package/templates/nova/tests/cases/components/states/Empty.cases.tsx +30 -0
- package/templates/nova/tests/cases/components/states/Error.cases.tsx +38 -0
- package/templates/nova/tests/cases/components/states/Loading.cases.tsx +15 -0
- package/templates/nova/tests/cases/components/states/MediaState.cases.tsx +29 -0
- package/templates/nova/tests/cases/components/states/SkeletonLayout.cases.tsx +20 -0
- package/templates/nova/tests/cases/components/states/Unavailable.cases.tsx +16 -0
- package/templates/nova/tests/cases/components/states/media/MediaError.cases.tsx +18 -0
- package/templates/nova/tests/cases/components/states/media/MediaPlaceholder.cases.tsx +87 -0
- package/templates/nova/tests/cases/sections/Overview.cases.tsx +8 -0
- package/templates/nova/tests/cases/sections/commerce/collection-list/CollectionList.cases.tsx +46 -0
- package/templates/nova/tests/cases/sections/commerce/collection-list/blocks/CollectionItem.cases.tsx +18 -0
- package/templates/nova/tests/cases/sections/commerce/featured-collection/FeaturedCollection.cases.tsx +46 -0
- package/templates/nova/tests/cases/sections/commerce/featured-product/FeaturedProduct.cases.tsx +40 -0
- package/templates/nova/tests/cases/sections/commerce/main-collection/MainCollection.cases.tsx +81 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/ProductDetails.cases.tsx +110 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/AddToCartBlock.cases.tsx +21 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductDescription.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductInventory.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductPrice.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductSku.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductTitle.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/Quantity.cases.tsx +20 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/Tab.cases.tsx +29 -0
- package/templates/nova/tests/cases/sections/commerce/product-details/blocks/VariantPickerBlock.cases.tsx +32 -0
- package/templates/nova/tests/cases/sections/commerce/product-grid/ProductGrid.cases.tsx +48 -0
- package/templates/nova/tests/cases/sections/commerce/product-recommendations/ProductRecommendations.cases.tsx +27 -0
- package/templates/nova/tests/cases/sections/commerce/recently-viewed/RecentlyViewed.cases.tsx +36 -0
- package/templates/nova/tests/cases/sections/content/article-body/ArticleBody.cases.tsx +32 -0
- package/templates/nova/tests/cases/sections/content/before-after/BeforeAfter.cases.tsx +40 -0
- package/templates/nova/tests/cases/sections/content/before-after/blocks/Comparison.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/content/blog-posts/BlogPosts.cases.tsx +47 -0
- package/templates/nova/tests/cases/sections/content/collection-links/CollectionLinks.cases.tsx +36 -0
- package/templates/nova/tests/cases/sections/content/faq/Faq.cases.tsx +38 -0
- package/templates/nova/tests/cases/sections/content/faq/blocks/FaqItem.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/content/feature-grid-blocks/FeatureGridBlocks.cases.tsx +31 -0
- package/templates/nova/tests/cases/sections/content/feature-highlights/FeatureHighlights.cases.tsx +26 -0
- package/templates/nova/tests/cases/sections/content/image-gallery/ImageGallery.cases.tsx +31 -0
- package/templates/nova/tests/cases/sections/content/image-with-text/ImageWithText.cases.tsx +35 -0
- package/templates/nova/tests/cases/sections/content/multicolumn/Multicolumn.cases.tsx +32 -0
- package/templates/nova/tests/cases/sections/content/page-body/PageBody.cases.tsx +31 -0
- package/templates/nova/tests/cases/sections/content/rich-text/RichTextSection.cases.tsx +33 -0
- package/templates/nova/tests/cases/sections/content/team-founders/TeamFounders.cases.tsx +31 -0
- package/templates/nova/tests/cases/sections/content/team-founders/blocks/Person.cases.tsx +18 -0
- package/templates/nova/tests/cases/sections/content/timeline/Timeline.cases.tsx +27 -0
- package/templates/nova/tests/cases/sections/content/timeline/blocks/Step.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/layout/announcement-bar/AnnouncementBar.cases.tsx +119 -0
- package/templates/nova/tests/cases/sections/layout/breadcrumbs/Breadcrumbs.cases.tsx +73 -0
- package/templates/nova/tests/cases/sections/layout/divider/Divider.cases.tsx +81 -0
- package/templates/nova/tests/cases/sections/layout/footer/Footer.cases.tsx +116 -0
- package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterBrand.cases.tsx +27 -0
- package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterMenu.cases.tsx +22 -0
- package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterText.cases.tsx +20 -0
- package/templates/nova/tests/cases/sections/layout/group/Group.cases.tsx +185 -0
- package/templates/nova/tests/cases/sections/layout/group/blocks/Accordion.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/layout/group/blocks/JumboText.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/layout/group/blocks/Spacer.cases.tsx +33 -0
- package/templates/nova/tests/cases/sections/layout/header/Header.cases.tsx +277 -0
- package/templates/nova/tests/cases/sections/marketing/collection-banner/CollectionBanner.cases.tsx +62 -0
- package/templates/nova/tests/cases/sections/marketing/hero/Hero.cases.tsx +138 -0
- package/templates/nova/tests/cases/sections/marketing/marquee/Marquee.cases.tsx +63 -0
- package/templates/nova/tests/cases/sections/marketing/promo-banner/PromoBanner.cases.tsx +87 -0
- package/templates/nova/tests/cases/sections/marketing/slideshow/Slideshow.cases.tsx +113 -0
- package/templates/nova/tests/cases/sections/marketing/slideshow/blocks/Slide.cases.tsx +26 -0
- package/templates/nova/tests/cases/sections/marketing/video-hero/VideoHero.cases.tsx +72 -0
- package/templates/nova/tests/cases/sections/trust/comparison-table/ComparisonTableSection.cases.tsx +42 -0
- package/templates/nova/tests/cases/sections/trust/comparison-table/blocks/CompareItem.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/trust/logo-list/LogoList.cases.tsx +29 -0
- package/templates/nova/tests/cases/sections/trust/logo-list/blocks/Logo.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/trust/product-reviews/ProductReviews.cases.tsx +51 -0
- package/templates/nova/tests/cases/sections/trust/shop-the-look/ShopTheLook.cases.tsx +51 -0
- package/templates/nova/tests/cases/sections/trust/shop-the-look/blocks/Hotspot.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/trust/testimonials/Testimonials.cases.tsx +61 -0
- package/templates/nova/tests/cases/sections/trust/testimonials/blocks/Testimonial.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/trust/trust-badges/TrustBadges.cases.tsx +30 -0
- package/templates/nova/tests/cases/sections/trust/ugc-gallery/UgcGallery.cases.tsx +32 -0
- package/templates/nova/tests/cases/sections/trust/ugc-gallery/blocks/UgcImage.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/utility/account/Account.cases.tsx +44 -0
- package/templates/nova/tests/cases/sections/utility/cart-items/CartItems.cases.tsx +43 -0
- package/templates/nova/tests/cases/sections/utility/cart-items/blocks/CartBadge.cases.tsx +16 -0
- package/templates/nova/tests/cases/sections/utility/contact-form/ContactFormSection.cases.tsx +52 -0
- package/templates/nova/tests/cases/sections/utility/contact-form/blocks/ContactDetail.cases.tsx +17 -0
- package/templates/nova/tests/cases/sections/utility/newsletter/NewsletterSection.cases.tsx +109 -0
- package/templates/nova/tests/cases/sections/utility/not-found/NotFound.cases.tsx +28 -0
- package/templates/nova/tests/cases/sections/utility/policy-page/PolicyPage.cases.tsx +29 -0
- package/templates/nova/tests/cases/sections/utility/search-results/SearchResultsSection.cases.tsx +39 -0
- package/templates/nova/tests/cases/sections/utility/store-locator/StoreLocator.cases.tsx +40 -0
- package/templates/nova/tests/cases/styles/Borders.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Breakpoints.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Colors.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Elevation.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Icon.cases.tsx +23 -0
- package/templates/nova/tests/cases/styles/Layout.cases.tsx +8 -0
- package/templates/nova/tests/cases/styles/Motion.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Overview.cases.tsx +8 -0
- package/templates/nova/tests/cases/styles/Radius.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/SharedThemeSettings.cases.tsx +19 -0
- package/templates/nova/tests/cases/styles/Spacing.cases.tsx +10 -0
- package/templates/nova/tests/cases/styles/Typography.cases.tsx +10 -0
- package/templates/nova/tests/cases.test.tsx +38 -0
- package/templates/nova/tests/components/PromoPopup.test.ts +21 -0
- package/templates/nova/tests/components/RichText.test.tsx +65 -0
- package/templates/nova/tests/components/formatDate.test.ts +10 -0
- package/templates/nova/tests/contract-locks.test.ts +23 -0
- package/templates/nova/tests/contracts.test.ts +153 -0
- package/templates/nova/tests/cookie-consent-privacy.test.tsx +128 -0
- package/templates/nova/tests/empty-store.test.tsx +81 -0
- package/templates/nova/tests/fixtures/commerce.ts +128 -0
- package/templates/nova/tests/fixtures/content.ts +58 -0
- package/templates/nova/tests/fixtures/index.ts +8 -0
- package/templates/nova/tests/fixtures/merch.ts +161 -0
- package/templates/nova/tests/fixtures/navigation.ts +125 -0
- package/templates/nova/tests/fixtures/promotion.ts +43 -0
- package/templates/nova/tests/fixtures/sample-store.json +1674 -0
- package/templates/nova/tests/fixtures/shop.ts +88 -0
- package/templates/nova/tests/fixtures/stress.ts +21 -0
- package/templates/nova/tests/inert.test.tsx +285 -0
- package/templates/nova/tests/line-add-friend.test.tsx +108 -0
- package/templates/nova/tests/localized-copy.test.ts +330 -0
- package/templates/nova/tests/product-template-blocks.test.ts +28 -0
- package/templates/nova/tests/rewards-launcher.test.tsx +401 -0
- package/templates/nova/tests/schema.test.ts +65 -0
- package/templates/nova/tests/schema.typetest.ts +7 -0
- package/templates/nova/tests/section-groups.test.ts +189 -0
- package/templates/nova/tests/sections/Group.test.tsx +37 -0
- package/templates/nova/tests/setup.ts +30 -0
- package/templates/nova/tests/starter.test.ts +56 -0
- package/templates/nova/tests/storefront-click-path.test.tsx +157 -0
- package/templates/nova/tests/styles/button-brand.test.ts +24 -0
- package/templates/nova/tests/styles/theme.test.ts +43 -0
- package/templates/nova/tests/styles/tokens.test.ts +35 -0
- package/templates/nova/tests/support/abi-ctx.ts +136 -0
- package/templates/nova/tests/support/block-stories.tsx +13 -0
- package/templates/nova/tests/support/case.tsx +102 -0
- package/templates/nova/tests/support/contracts.ts +30 -0
- package/templates/nova/tests/support/foundation-pages.tsx +260 -0
- package/templates/nova/tests/support/ia-pages.tsx +154 -0
- package/templates/nova/tests/support/listing-demo.tsx +29 -0
- package/templates/nova/tests/support/presets.ts +14 -0
- package/templates/nova/tests/support/sections.ts +36 -0
- package/templates/nova/tests/support/story-content.ts +321 -0
- package/templates/nova/tests/support/story-helpers.tsx +37 -0
- package/templates/nova/tests/support/with-nova.tsx +26 -0
- package/templates/nova/tests/theme-config.test.ts +82 -0
- package/templates/nova/tests/theme-integrity.test.ts +315 -0
- package/templates/nova/theme.config.ts +134 -0
- package/templates/nova/theme.manifest.json +17495 -0
- package/templates/nova/tsconfig.json +19 -0
- package/templates/nova/vite.abi-ssr.config.ts +37 -0
- package/templates/nova/vite.abi.config.ts +26 -0
- package/templates/nova/vitest.config.ts +20 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Theme ABI v2 RenderContext for tests: the shape the platform hands `render(request, ctx)`,
|
|
3
|
+
* backed by in-memory ABI records. Records every data call so a test can assert the plan.
|
|
4
|
+
*/
|
|
5
|
+
import type { RenderContext } from '@tanqory/theme-abi'
|
|
6
|
+
import manifestJson from '../../lib/abi/generated/manifest.json'
|
|
7
|
+
|
|
8
|
+
type Obj = Record<string, unknown>
|
|
9
|
+
const manifest = manifestJson as unknown as {
|
|
10
|
+
routes: Record<string, string>
|
|
11
|
+
templates: Record<string, { route: string; regions: string[] }>
|
|
12
|
+
regions: Record<string, { scope: string }>
|
|
13
|
+
contentVersion: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const money = (amount: string, currencyCode = 'USD') => ({ amount, currencyCode })
|
|
17
|
+
export const conn = <T,>(nodes: T[]) => ({ nodes, pageInfo: { hasNextPage: false } })
|
|
18
|
+
|
|
19
|
+
export function product(handle: string, title: string, price = '49.00') {
|
|
20
|
+
return {
|
|
21
|
+
id: `p-${handle}`, handle, title, descriptionHtml: `<p>${title} description</p>`, tags: [], availableForSale: true, vendor: 'Halden', productType: 'Home',
|
|
22
|
+
featuredImage: { url: `https://cdn.example/${handle}.jpg`, altText: title, width: 800, height: 800 },
|
|
23
|
+
images: [{ url: `https://cdn.example/${handle}.jpg`, altText: title, width: 800, height: 800 }],
|
|
24
|
+
options: [{ name: 'Title', values: ['Default Title'] }],
|
|
25
|
+
variants: conn([{ id: `v-${handle}`, title: 'Default Title', availableForSale: true, price: money(price), compareAtPrice: null, selectedOptions: [{ name: 'Title', value: 'Default Title' }], sku: `SKU-${handle}` }]),
|
|
26
|
+
priceRange: { minVariantPrice: money(price), maxVariantPrice: money(price) },
|
|
27
|
+
seo: {},
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function collection(handle: string, title: string, products: ReturnType<typeof product>[]) {
|
|
32
|
+
return { id: `c-${handle}`, handle, title, descriptionHtml: '', image: null, products: { ...conn(products), totalCount: products.length }, seo: {} }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function menu(handle: string, items: [string, string][]) {
|
|
36
|
+
return { handle, title: handle, items: items.map(([title, url], i) => ({ id: `${handle}-${i}`, title, url, type: 'HTTP', items: [] })) }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const SHOP = {
|
|
40
|
+
name: 'Halden & Co.', description: 'Linen and stoneware', primaryDomain: { url: 'https://shop.example' },
|
|
41
|
+
paymentSettings: { currencyCode: 'USD', enabledPresentmentCurrencies: ['USD'] }, shipsToCountries: ['US', 'TH'], brand: null,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface Store {
|
|
45
|
+
shop?: Obj
|
|
46
|
+
menus?: ReturnType<typeof menu>[]
|
|
47
|
+
products?: ReturnType<typeof product>[]
|
|
48
|
+
collections?: ReturnType<typeof collection>[]
|
|
49
|
+
/** The store's PUBLISHED policies (ABI `Policy`); an unpublished one is simply absent. */
|
|
50
|
+
policies?: { handle: string; title: string; body: string; url: string }[]
|
|
51
|
+
cart?: Obj | null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The page the platform composes: regions in template order, from `regions` (shared) / the template's own. */
|
|
55
|
+
export function pageOf(template: string, sections: { region: string; sections: Obj[] }[]) {
|
|
56
|
+
const order = manifest.templates[template]!.regions
|
|
57
|
+
return { contentVersion: manifest.contentVersion, template, regions: order.flatMap((r) => sections.filter((s) => s.region === r)) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function abiCtx(o: {
|
|
61
|
+
kind: string
|
|
62
|
+
handle?: string
|
|
63
|
+
template?: string
|
|
64
|
+
url?: string
|
|
65
|
+
params?: Record<string, string>
|
|
66
|
+
page: ReturnType<typeof pageOf>
|
|
67
|
+
settings?: Obj
|
|
68
|
+
store?: Store
|
|
69
|
+
mode?: 'serve' | 'preview'
|
|
70
|
+
}): { ctx: RenderContext; ops: string[] } {
|
|
71
|
+
const ops: string[] = []
|
|
72
|
+
const s = o.store ?? {}
|
|
73
|
+
const rec = <T,>(op: string, v: T) => { ops.push(op); return Promise.resolve(v) }
|
|
74
|
+
const products = s.products ?? []
|
|
75
|
+
const collections = s.collections ?? []
|
|
76
|
+
const menus = Object.fromEntries((s.menus ?? []).map((m) => [m.handle, m]))
|
|
77
|
+
const byHandle = <T extends { handle: string }>(list: T[], h: string) => list.find((x) => x.handle === h) ?? null
|
|
78
|
+
const canonical = o.url ?? `https://shop.example/${o.kind === 'home' ? '' : `${o.kind}s/${o.handle ?? ''}`}`
|
|
79
|
+
const resource = (): Obj => {
|
|
80
|
+
switch (o.kind) {
|
|
81
|
+
case 'product': return { kind: 'product', product: byHandle(products, o.handle ?? '') }
|
|
82
|
+
case 'collection': return { kind: 'collection', collection: byHandle(collections, o.handle ?? '') }
|
|
83
|
+
case 'policy': return { kind: 'policy', policy: byHandle(s.policies ?? [], o.handle ?? '') }
|
|
84
|
+
case 'cart': return { kind: 'cart', cart: s.cart ?? null }
|
|
85
|
+
default: return { kind: o.kind }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const ctx = {
|
|
89
|
+
abi: '2.0',
|
|
90
|
+
mode: o.mode ?? 'serve',
|
|
91
|
+
route: { kind: o.kind, ...(o.handle ? { handle: o.handle } : {}), template: o.template ?? manifest.routes[o.kind], params: o.params ?? {}, canonical },
|
|
92
|
+
request: { method: 'GET', url: canonical, headers: {}, hasCart: false, isSignedIn: false, isPreview: o.mode === 'preview' },
|
|
93
|
+
locale: { language: 'en', country: 'US', market: 'us', currency: 'USD', availableLanguages: ['en'], timeZone: 'UTC' },
|
|
94
|
+
consent: { known: false, analytics: false, marketing: false, functional: true },
|
|
95
|
+
capabilities: new Set(['data:shop', 'data:localization', 'data:catalog', 'data:content', 'data:search', 'data:cart', 'data:products.recommendations']),
|
|
96
|
+
content: { revision: 'sha256:test', contentVersion: manifest.contentVersion, settings: o.settings ?? {}, page: o.page },
|
|
97
|
+
data: {
|
|
98
|
+
dataVersion: 'test',
|
|
99
|
+
// The platform resolves every `resolve: true` setting into refs; the theme's planner reads them, and
|
|
100
|
+
// fetches in round 2 what the snapshot did not answer. Tests leave refs empty: round 2 is exercised.
|
|
101
|
+
snapshot: () => rec('snapshot', { shop: s.shop ?? SHOP, menus, resource: resource(), refs: {}, pixels: [], denied: [] }),
|
|
102
|
+
shop: { get: () => rec('shop.get', s.shop ?? SHOP), menus: (h?: string[]) => rec(`shop.menus:${(h ?? []).join(',')}`, h ? (s.menus ?? []).filter((m) => h.includes(m.handle)) : s.menus ?? []) },
|
|
103
|
+
catalog: {
|
|
104
|
+
product: (h: string) => rec(`catalog.product:${h}`, byHandle(products, h)),
|
|
105
|
+
collection: (h: string) => rec(`catalog.collection:${h}`, byHandle(collections, h)),
|
|
106
|
+
collections: () => rec('catalog.collections', conn(collections)),
|
|
107
|
+
search: (a: { query: string }) => rec(`catalog.search:${a.query}`, { query: a.query, products: conn(products), pages: conn([]), articles: conn([]), collections: conn([]) }),
|
|
108
|
+
recommendations: (h: string) => rec(`catalog.recommendations:${h}`, products.filter((p) => p.handle !== h).slice(0, 4)),
|
|
109
|
+
},
|
|
110
|
+
content: { page: (h: string) => rec(`content.page:${h}`, null), blog: (h: string) => rec(`content.blog:${h}`, null), policy: (h: string) => rec(`content.policy:${h}`, byHandle(s.policies ?? [], h)) },
|
|
111
|
+
customer: { accountUrl: (p?: string) => `/account${p ? `/${p}` : ''}` },
|
|
112
|
+
},
|
|
113
|
+
assets: { base: 'https://shop.example/a/test/client', manifest: { main: { file: 'assets/main.js', kind: 'script', isEntry: true, css: ['assets/main.css'], imports: [], integrity: 'sha256-x' }, 'assets/main.css': { file: 'assets/main.css', kind: 'style', integrity: 'sha256-y' } } },
|
|
114
|
+
cache: { hint() {} },
|
|
115
|
+
log: { debug() {}, info() {}, warn() {}, error: (msg: string, f?: unknown) => { throw new Error(`${msg} ${JSON.stringify(f)}`) } },
|
|
116
|
+
now: () => Date.UTC(2026, 9, 13),
|
|
117
|
+
release: { id: 'r', artifactDigest: 'd' },
|
|
118
|
+
actions: undefined,
|
|
119
|
+
signal: new AbortController().signal,
|
|
120
|
+
} as unknown as RenderContext
|
|
121
|
+
return { ctx, ops }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The server HTML split into what a test reads. */
|
|
125
|
+
export async function serverHtml(render: (r: Request, c: RenderContext) => Promise<Response>, ctx: RenderContext) {
|
|
126
|
+
const res = await render(new Request(ctx.request.url), ctx)
|
|
127
|
+
const html = await res.text()
|
|
128
|
+
return {
|
|
129
|
+
status: res.status,
|
|
130
|
+
html,
|
|
131
|
+
headers: res.headers,
|
|
132
|
+
root: html.match(/<div id="root">([\s\S]*)<\/div>\s*<\/body>/)?.[1] ?? '',
|
|
133
|
+
state: html.match(/<script type="application\/json" id="tq-state">([\s\S]*?)<\/script>/)?.[1] ?? '',
|
|
134
|
+
rules: html.match(/<style id="tq-style-rules">([\s\S]*?)<\/style>/)?.[1] ?? '',
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { StoryObj } from './case'
|
|
2
|
+
import { defaultsOf, type Schema } from '@tanqory/theme-kit'
|
|
3
|
+
|
|
4
|
+
/** A block's Playground case: the block schema's defaults under the case's own values. */
|
|
5
|
+
export function blockPlayground<P extends object>(schema: Schema, extra: Partial<P> = {}, _parent?: string): StoryObj<P> {
|
|
6
|
+
return { name: 'Playground', args: { ...defaultsOf(schema), ...extra } as P } as unknown as StoryObj<P>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const blockFrame = (S: () => JSX.Element) => (
|
|
10
|
+
<div style={{ padding: 32, maxWidth: 720 }}>
|
|
11
|
+
<S />
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test cases without a component workbench. A case file (`*.cases.tsx`) exports
|
|
3
|
+
* a `meta` default and named cases; `cases.test.tsx` renders every case inside
|
|
4
|
+
* <NovaProvider>, runs its `play` and checks it with axe.
|
|
5
|
+
*
|
|
6
|
+
* The shapes follow the Component Story Format so the cases read the same way
|
|
7
|
+
* they were written: `args`, `render`, `decorators`, `parameters.nova`, `play`.
|
|
8
|
+
*/
|
|
9
|
+
import { createElement, type ComponentType, type ReactNode } from 'react'
|
|
10
|
+
import { render } from '@testing-library/react'
|
|
11
|
+
import userEventLib from '@testing-library/user-event'
|
|
12
|
+
import { vi } from 'vitest'
|
|
13
|
+
|
|
14
|
+
export { expect } from 'vitest'
|
|
15
|
+
export { within, waitFor, screen, fireEvent } from '@testing-library/react'
|
|
16
|
+
export const userEvent = userEventLib
|
|
17
|
+
export const fn = vi.fn
|
|
18
|
+
|
|
19
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
20
|
+
type PropsOf<C> = C extends ComponentType<infer P> ? P : Record<string, any>
|
|
21
|
+
type ArgsOf<M> = M extends { component: infer C } ? PropsOf<C> : M extends { args: infer A } ? A : Record<string, any>
|
|
22
|
+
|
|
23
|
+
export interface NovaParams {
|
|
24
|
+
locale?: string
|
|
25
|
+
mode?: 'storefront' | 'editor'
|
|
26
|
+
scheme?: 'light' | 'dark'
|
|
27
|
+
motion?: 'standard' | 'reduced'
|
|
28
|
+
theme?: object
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CaseContext<A = any> {
|
|
32
|
+
args: A
|
|
33
|
+
globals: Record<string, any>
|
|
34
|
+
parameters: Record<string, any> & { nova?: NovaParams }
|
|
35
|
+
canvasElement: HTMLElement
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type CaseComponent = () => JSX.Element
|
|
39
|
+
export type Decorator<A = any> = (Story: CaseComponent, ctx: CaseContext<A>) => ReactNode
|
|
40
|
+
|
|
41
|
+
export interface Meta<C = any> {
|
|
42
|
+
title?: string
|
|
43
|
+
component?: C
|
|
44
|
+
args?: Partial<PropsOf<C>>
|
|
45
|
+
parameters?: Record<string, any>
|
|
46
|
+
decorators?: Decorator<PropsOf<C>>[]
|
|
47
|
+
render?: (args: PropsOf<C>, ctx: CaseContext<PropsOf<C>>) => ReactNode
|
|
48
|
+
play?: (ctx: CaseContext<PropsOf<C>>) => Promise<void> | void
|
|
49
|
+
tags?: string[]
|
|
50
|
+
globals?: Record<string, any>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface StoryObj<M = any> {
|
|
54
|
+
name?: string
|
|
55
|
+
args?: Partial<ArgsOf<M>>
|
|
56
|
+
render?: (args: ArgsOf<M>, ctx: CaseContext<ArgsOf<M>>) => ReactNode
|
|
57
|
+
play?: (ctx: CaseContext<ArgsOf<M>>) => Promise<void> | void
|
|
58
|
+
parameters?: Record<string, any> & { nova?: NovaParams }
|
|
59
|
+
decorators?: Decorator<ArgsOf<M>>[]
|
|
60
|
+
tags?: string[]
|
|
61
|
+
globals?: Record<string, any>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type CaseModule = { default: Meta } & Record<string, unknown>
|
|
65
|
+
|
|
66
|
+
/** The named cases of a module (every named export that is an object). */
|
|
67
|
+
export function casesOf(mod: CaseModule): [string, StoryObj][] {
|
|
68
|
+
return Object.entries(mod).filter(([k, v]) => k !== 'default' && v !== null && typeof v === 'object' && !Array.isArray(v)) as [string, StoryObj][]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The args a case renders with: meta args under the case's own. */
|
|
72
|
+
export function caseArgs(mod: CaseModule, name: string): Record<string, unknown> {
|
|
73
|
+
const story = mod[name] as StoryObj | undefined
|
|
74
|
+
if (!story) throw new Error(`no case "${name}"`)
|
|
75
|
+
return { ...(mod.default.args ?? {}), ...(story.args ?? {}) }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Render one case. `wrap` is the outermost decorator (the NovaProvider), applied
|
|
80
|
+
* after the case's and the meta's own — the same order a workbench uses.
|
|
81
|
+
*/
|
|
82
|
+
export async function runCase(mod: CaseModule, story: StoryObj, wrap: Decorator): Promise<CaseContext> {
|
|
83
|
+
const meta = mod.default
|
|
84
|
+
const args = { ...(meta.args ?? {}), ...(story.args ?? {}) }
|
|
85
|
+
const ctx: CaseContext = {
|
|
86
|
+
args,
|
|
87
|
+
globals: { ...(meta.globals ?? {}), ...(story.globals ?? {}) },
|
|
88
|
+
parameters: { ...(meta.parameters ?? {}), ...(story.parameters ?? {}) },
|
|
89
|
+
canvasElement: document.body,
|
|
90
|
+
}
|
|
91
|
+
const draw = story.render ?? meta.render
|
|
92
|
+
let Current: CaseComponent = () => (draw ? draw(args, ctx) : meta.component ? createElement(meta.component as ComponentType<any>, args) : null) as JSX.Element
|
|
93
|
+
for (const dec of [...(story.decorators ?? []), ...(meta.decorators ?? []), wrap]) {
|
|
94
|
+
const Inner: CaseComponent = Current
|
|
95
|
+
Current = (): JSX.Element => dec(Inner, ctx) as JSX.Element
|
|
96
|
+
}
|
|
97
|
+
const { container } = render(createElement(Current))
|
|
98
|
+
ctx.canvasElement = container
|
|
99
|
+
const play = story.play ?? meta.play
|
|
100
|
+
if (play) await play(ctx)
|
|
101
|
+
return ctx
|
|
102
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AcceptedDeviation, ValidateOptions } from '../../scripts/compiler/contracts/validate'
|
|
2
|
+
import theme from '../../theme.config'
|
|
3
|
+
import { BLOCK_SCHEMAS } from './sections'
|
|
4
|
+
import { promotionPopupSchema } from '../../config/promotion-popup.schema'
|
|
5
|
+
|
|
6
|
+
/** The design families Nova implements in full (every section in them is built). */
|
|
7
|
+
export const IMPLEMENTED_FAMILIES = ['12A', '12B', '12C', '12D', '12E', '12F', '12G', '13'] as const
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Differences between code and contract that are ACCEPTED, each with the
|
|
11
|
+
* reason. Keep this list short and reviewed: every entry is a place where the
|
|
12
|
+
* code knowingly differs from the approved contract.
|
|
13
|
+
*/
|
|
14
|
+
export const ACCEPTED_DEVIATIONS: readonly AcceptedDeviation[] = [
|
|
15
|
+
{
|
|
16
|
+
code: 'invalid-block',
|
|
17
|
+
where: 'block:footer-line@footer',
|
|
18
|
+
reason:
|
|
19
|
+
"army #7740 (main #467): LINE's Add friend button in the footer was added after the approved 12A design, which has no footer-line row. An addition, not a change: no locked id, key or option moves. Lock it when the design package gains the block.",
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
/** Everything the contract validator compares: the theme's own schemas. */
|
|
24
|
+
export const VALIDATION_INPUT: ValidateOptions = {
|
|
25
|
+
families: IMPLEMENTED_FAMILIES,
|
|
26
|
+
sections: theme.sections.map((s) => s.schema),
|
|
27
|
+
blocks: BLOCK_SCHEMAS,
|
|
28
|
+
globals: [promotionPopupSchema],
|
|
29
|
+
accepted: ACCEPTED_DEVIATIONS,
|
|
30
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
|
2
|
+
import type { StoryObj } from './case'
|
|
3
|
+
import foundations from '../../.tanqory/contracts/nova-01.json'
|
|
4
|
+
import { contrastRatio, THEME_DEFAULTS, type NovaThemeSettings } from '../../src/styles/theme'
|
|
5
|
+
import { NovaProvider } from '../../src/styles/NovaProvider'
|
|
6
|
+
import { Button } from '../../src/components/base/Button'
|
|
7
|
+
import { Badge } from '../../src/components/base/Badge'
|
|
8
|
+
import { Input } from '../../src/components/base/Input'
|
|
9
|
+
import { Chip } from '../../src/components/base/Chip'
|
|
10
|
+
import { Icon } from '../../src/styles/Icon'
|
|
11
|
+
import { Container } from '../../src/styles/Container'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Foundations render FROM the approved Nova 01 tables (generated/nova-01.json)
|
|
15
|
+
* beside the live computed token, so the page itself shows any drift.
|
|
16
|
+
*/
|
|
17
|
+
const F = foundations as unknown as {
|
|
18
|
+
colors: { n: string; v: string; role: string; r: string }[]
|
|
19
|
+
typeScale: { k: string; c: number; d: number; l: number; m: number; lh: string; w: number; tr: string }[]
|
|
20
|
+
typeRules: { t: string; d: string }[]
|
|
21
|
+
radii: { k: string; v: string }[]
|
|
22
|
+
radiusMap: { c: string; d: string; t: string; m: string }[]
|
|
23
|
+
gaps: { k: string; v: number }[]
|
|
24
|
+
sectionSpace: { k: string; d: string; m: string }[]
|
|
25
|
+
containers: { k: string; v: string }[]
|
|
26
|
+
ratios: { k: string; v: string; n: string }[]
|
|
27
|
+
motion: { k: string; d: string; e: string; p: string; r: string }[]
|
|
28
|
+
principles: { t: string; d: string }[]
|
|
29
|
+
tokens: { family: string; name: string; value: string }[]
|
|
30
|
+
dark: { k: string; l: string; d: string }[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Design names that the code keeps under their pre-package names (Nova 01 → tokens.css header). */
|
|
34
|
+
const CODE_NAME: Record<string, string> = { '--color-border-strong': '--color-border-emphasis', '--color-success-bg': '--color-success-surface', '--color-warning-bg': '--color-warning-surface', '--color-danger-bg': '--color-danger-surface', '--color-button-primary-bg': '--color-button', '--color-button-secondary-border': '--color-border-input' }
|
|
35
|
+
|
|
36
|
+
export const page = { padding: 'clamp(16px, 4vw, 32px)', display: 'grid', gap: 32, maxWidth: 1200, minWidth: 0, gridTemplateColumns: 'minmax(0, 1fr)' } as const
|
|
37
|
+
export const mono = { fontFamily: 'var(--font-mono)', fontSize: 12 } as const
|
|
38
|
+
export const muted = { color: 'var(--color-fg-muted)', fontSize: 13 } as const
|
|
39
|
+
|
|
40
|
+
export function Table({ head, rows }: { head: string[]; rows: ReactNode[][] }) {
|
|
41
|
+
return (
|
|
42
|
+
<div style={{ overflowX: 'auto', maxWidth: '100%' }} tabIndex={0} role="region" aria-label={head.join(', ')}>
|
|
43
|
+
<table style={{ borderCollapse: 'collapse', width: '100%', fontSize: 13 }}>
|
|
44
|
+
<thead>
|
|
45
|
+
<tr>{head.map((h) => <th key={h} style={{ textAlign: 'start', padding: '8px 12px 8px 0', borderBottom: '1px solid var(--color-fg)', ...mono }}>{h}</th>)}</tr>
|
|
46
|
+
</thead>
|
|
47
|
+
<tbody>
|
|
48
|
+
{rows.map((r, i) => (
|
|
49
|
+
<tr key={i}>{r.map((c, j) => <td key={j} style={{ padding: '10px 12px 10px 0', borderBottom: '1px solid var(--color-border)', verticalAlign: 'top' }}>{c}</td>)}</tr>
|
|
50
|
+
))}
|
|
51
|
+
</tbody>
|
|
52
|
+
</table>
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type Story = StoryObj
|
|
58
|
+
|
|
59
|
+
function ColourPage() {
|
|
60
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
61
|
+
const [vals, setVals] = useState<Record<string, string>>({})
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!ref.current) return
|
|
64
|
+
const cs = getComputedStyle(ref.current)
|
|
65
|
+
setVals(Object.fromEntries(F.colors.map((c) => CODE_NAME[c.n] ?? c.n).map((n) => [n, cs.getPropertyValue(n).trim()])))
|
|
66
|
+
}, [])
|
|
67
|
+
const norm = (v: string) => v.toLowerCase().replace(/\s/g, '')
|
|
68
|
+
return (
|
|
69
|
+
<div ref={ref} style={page}>
|
|
70
|
+
<h1 className="nv-h2">Colour roles</h1>
|
|
71
|
+
<p style={muted}>Design value from Nova 01 beside the live token. "≠" marks a deliberate deviation recorded in tokens.css (two inks darkened so the role's contrast promise holds on every surface).</p>
|
|
72
|
+
<Table
|
|
73
|
+
head={['swatch', 'token', 'role', 'design', 'code', 'on bg']}
|
|
74
|
+
rows={F.colors.map((c) => {
|
|
75
|
+
const code = CODE_NAME[c.n] ?? c.n
|
|
76
|
+
const live = vals[code] ?? ''
|
|
77
|
+
const same = norm(live) === norm(c.v) || !live.startsWith('#')
|
|
78
|
+
return [
|
|
79
|
+
<span style={{ display: 'block', width: 40, height: 28, borderRadius: 4, background: `var(${code})`, boxShadow: 'inset 0 0 0 1px rgba(0,0,0,.08)' }} />,
|
|
80
|
+
<code style={mono}>{c.n}{code !== c.n ? ` → ${code}` : ''}</code>,
|
|
81
|
+
c.role,
|
|
82
|
+
<code style={mono}>{c.v}</code>,
|
|
83
|
+
<code style={mono}>{live}{same ? '' : ' ≠'}</code>,
|
|
84
|
+
live.startsWith('#') && live.length === 7 ? `${contrastRatio(live, '#ffffff').toFixed(1)}:1` : '',
|
|
85
|
+
]
|
|
86
|
+
})}
|
|
87
|
+
/>
|
|
88
|
+
<h2 className="nv-h3">Dark scheme (opt-in)</h2>
|
|
89
|
+
<Table head={['token', 'light', 'dark']} rows={F.dark.map((d) => [<code style={mono}>{d.k}</code>, <code style={mono}>{d.l}</code>, <code style={mono}>{d.d}</code>])} />
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
export const Colors: Story = { render: () => <ColourPage /> }
|
|
94
|
+
|
|
95
|
+
export const Typography: Story = {
|
|
96
|
+
render: () => (
|
|
97
|
+
<div style={page}>
|
|
98
|
+
<h1 className="nv-h2">Type scale · Geist</h1>
|
|
99
|
+
<Table
|
|
100
|
+
head={['role', 'compact', 'default', 'large', '390', 'lh', 'wt', 'tracking', 'specimen']}
|
|
101
|
+
rows={F.typeScale.map((t) => [
|
|
102
|
+
<code style={mono}>{t.k}</code>, t.c, t.d, t.l, t.m, t.lh, t.w, t.tr,
|
|
103
|
+
<span style={{ fontSize: Math.min(t.d, 48), lineHeight: t.lh, fontWeight: t.w, letterSpacing: t.tr === '0' ? 0 : t.tr.replace('−', '-') }}>Linen, washed</span>,
|
|
104
|
+
])}
|
|
105
|
+
/>
|
|
106
|
+
<div style={{ display: 'grid', gap: 8 }}>
|
|
107
|
+
{F.typeRules.map((r) => <p key={r.t}><strong style={{ fontWeight: 500 }}>{r.t}.</strong> <span style={muted}>{r.d}</span></p>)}
|
|
108
|
+
</div>
|
|
109
|
+
<div lang="th" style={{ display: 'grid', gap: 8 }}>
|
|
110
|
+
<p className="nv-h3">ผ้าลินินยิ่งซักยิ่งนุ่ม</p>
|
|
111
|
+
<p>ซักน้ำเย็น ตากในที่ร่ม ไม่ต้องรีด เส้นใยลินินจะนุ่มขึ้นทุกครั้งที่ซัก และแข็งแรงขึ้นเมื่อเปียก</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div dir="rtl" lang="ar" style={{ display: 'grid', gap: 8 }}>
|
|
114
|
+
<p className="nv-h3">الكتان يتحسن مع كل غسلة</p>
|
|
115
|
+
<p>اغسل بماء بارد وجفف على الحبل ولا تستخدم المكواة.</p>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const Spacing: Story = {
|
|
122
|
+
render: () => (
|
|
123
|
+
<div style={page}>
|
|
124
|
+
<h1 className="nv-h2">Gaps</h1>
|
|
125
|
+
<Table head={['token', 'px', '']} rows={F.gaps.map((g) => [<code style={mono}>--gap-{g.k}</code>, g.v, <span style={{ display: 'block', height: 12, width: g.v * 4, background: 'var(--color-fg)' }} />])} />
|
|
126
|
+
<h2 className="nv-h3">Section spacing (shared IDs none · small · medium · large · xl)</h2>
|
|
127
|
+
<Table head={['rung', 'desktop', '390']} rows={F.sectionSpace.map((s) => [s.k, s.d, s.m])} />
|
|
128
|
+
</div>
|
|
129
|
+
),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const Radius: Story = {
|
|
133
|
+
render: () => (
|
|
134
|
+
<div style={page}>
|
|
135
|
+
<h1 className="nv-h2">Radius</h1>
|
|
136
|
+
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
|
137
|
+
{F.radii.map((r) => (
|
|
138
|
+
<figure key={r.k} style={{ margin: 0, textAlign: 'center' }}>
|
|
139
|
+
<div style={{ width: 72, height: 72, background: 'var(--color-bg-muted)', border: '1px solid var(--color-border-input)', borderRadius: r.v }} />
|
|
140
|
+
<figcaption style={{ ...mono, marginTop: 6 }}>{r.k} {r.v}</figcaption>
|
|
141
|
+
</figure>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
144
|
+
<Table head={['component', 'default', 'token', 'setting']} rows={F.radiusMap.map((r) => [r.c, r.d, <code style={mono}>{r.t}</code>, r.m])} />
|
|
145
|
+
</div>
|
|
146
|
+
),
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const Motion: Story = {
|
|
150
|
+
render: () => (
|
|
151
|
+
<div style={page}>
|
|
152
|
+
<h1 className="nv-h2">Motion</h1>
|
|
153
|
+
<Table head={['what', 'duration', 'easing', 'property', 'reduced motion']} rows={F.motion.map((m) => [m.k, m.d, <code style={mono}>{m.e}</code>, m.p, m.r])} />
|
|
154
|
+
</div>
|
|
155
|
+
),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
type ThemeArgs = Required<NovaThemeSettings>
|
|
159
|
+
export const ThemeSettings: StoryObj<ThemeArgs> = {
|
|
160
|
+
args: { ...THEME_DEFAULTS, colorPrimary: '#111111' },
|
|
161
|
+
parameters: { docs: { description: { story: 'Level 1 theme settings with their persisted IDs (e.g. buttonRadius none · small · medium · pill). A default rung writes nothing.' } } },
|
|
162
|
+
render: (args) => (
|
|
163
|
+
<NovaProvider theme={args} locale="en">
|
|
164
|
+
<div style={page}>
|
|
165
|
+
<h1 className="nv-h1">Settings preview</h1>
|
|
166
|
+
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
|
167
|
+
<Button>Add to cart</Button>
|
|
168
|
+
<Button variant="secondary">View all</Button>
|
|
169
|
+
<Button variant="tertiary">Learn more</Button>
|
|
170
|
+
<Chip selected>Selected</Chip>
|
|
171
|
+
<Badge tone="sale">−21%</Badge>
|
|
172
|
+
<Badge tone="new">New</Badge>
|
|
173
|
+
<Icon name="bag" size="lg" />
|
|
174
|
+
</div>
|
|
175
|
+
<div style={{ maxWidth: 320 }}><Input placeholder="you@example.com" aria-label="Email" /></div>
|
|
176
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16 }}>
|
|
177
|
+
{[0, 1, 2].map((i) => <div key={i} style={{ aspectRatio: '1', background: 'var(--color-media-bg)', borderRadius: 'var(--radius-media)' }} />)}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</NovaProvider>
|
|
181
|
+
),
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const Borders: Story = {
|
|
185
|
+
render: () => (
|
|
186
|
+
<div style={page}>
|
|
187
|
+
<h1 className="nv-h2">Borders</h1>
|
|
188
|
+
<p style={muted}>Hairlines separate; they never decorate. Controls use the 3:1 input grey; focus is a fixed 2px ring that is never the brand.</p>
|
|
189
|
+
<Table
|
|
190
|
+
head={['role', 'token', 'sample']}
|
|
191
|
+
rows={[
|
|
192
|
+
['Decorative divider', '--color-border · --border-subtle', <span style={{ display: 'block', width: 160, borderTop: 'var(--border-subtle)' }} />],
|
|
193
|
+
['Control boundary (≥ 3:1)', '--color-border-input · --input-border', <span style={{ display: 'block', width: 160, height: 32, border: 'var(--input-border)', borderRadius: 'var(--input-radius)' }} />],
|
|
194
|
+
['Region control (12A)', '--color-border-control', <span style={{ display: 'block', width: 160, height: 32, border: '1px solid var(--color-border-control)', borderRadius: 'var(--radius-md)' }} />],
|
|
195
|
+
['Selected / emphasis', '--color-border-emphasis · --border-strong', <span style={{ display: 'block', width: 160, borderTop: 'var(--border-strong)' }} />],
|
|
196
|
+
['Checkbox / radio stroke', '--border-width-control (1.5px)', <span style={{ display: 'block', width: 20, height: 20, border: 'var(--border-width-control) solid var(--color-border-input)', borderRadius: 'var(--radius-sm)' }} />],
|
|
197
|
+
['Focus ring', '--color-focus · 2px + 2px offset', <span style={{ display: 'block', width: 120, height: 32, borderRadius: 'var(--radius-md)', outline: '2px solid var(--color-focus)', outlineOffset: 2 }} />],
|
|
198
|
+
]}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
),
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const Elevation: Story = {
|
|
205
|
+
render: () => (
|
|
206
|
+
<div style={page}>
|
|
207
|
+
<h1 className="nv-h2">Elevation</h1>
|
|
208
|
+
<p style={muted}>Nothing in the page flow casts a shadow. Shadows exist only for surfaces that float over content, and every floating surface sits on one z-index ladder.</p>
|
|
209
|
+
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap' }}>
|
|
210
|
+
{(['popover', 'floating', 'drawer', 'modal'] as const).map((k) => (
|
|
211
|
+
<figure key={k} style={{ margin: 0, display: 'grid', gap: 8 }}>
|
|
212
|
+
<div style={{ width: 160, height: 96, background: 'var(--color-bg-elevated)', borderRadius: 'var(--radius-md)', boxShadow: `var(--shadow-${k})` }} />
|
|
213
|
+
<figcaption style={mono}>--shadow-{k}</figcaption>
|
|
214
|
+
</figure>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
217
|
+
<h2 className="nv-h3">Layers (one ladder)</h2>
|
|
218
|
+
<Table head={['token', 'value']} rows={F.tokens.filter((t) => t.family === 'layer').map((t) => [<code style={mono}>{t.name}</code>, t.value])} />
|
|
219
|
+
</div>
|
|
220
|
+
),
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const Breakpoints: Story = {
|
|
224
|
+
render: () => (
|
|
225
|
+
<div style={page}>
|
|
226
|
+
<h1 className="nv-h2">Breakpoints & containers</h1>
|
|
227
|
+
<Table
|
|
228
|
+
head={['width', 'role', 'page gutter']}
|
|
229
|
+
rows={[
|
|
230
|
+
['390', 'reference phone (below 640: phone layout)', '16'],
|
|
231
|
+
['640 · --bp-tablet', 'tablet', '24'],
|
|
232
|
+
['1024 · --bp-desktop', 'desktop layout (nav, mega menu, 12-column grids)', '40'],
|
|
233
|
+
['1280', 'reference desktop · standard container', '40'],
|
|
234
|
+
]}
|
|
235
|
+
/>
|
|
236
|
+
<h2 className="nv-h3">Containers</h2>
|
|
237
|
+
<Table head={['width', 'max']} rows={F.containers.map((c) => [c.k, c.v])} />
|
|
238
|
+
<h2 className="nv-h3">Media ratios</h2>
|
|
239
|
+
<div style={{ display: 'flex', gap: 16, alignItems: 'end', flexWrap: 'wrap' }}>
|
|
240
|
+
{F.ratios.map((r) => (
|
|
241
|
+
<figure key={r.k} style={{ margin: 0, width: 140 }}>
|
|
242
|
+
<div style={{ aspectRatio: r.v, background: 'var(--color-media-bg)', borderRadius: 'var(--radius-media)' }} />
|
|
243
|
+
<figcaption style={{ ...mono, marginTop: 6 }}>{r.k} {r.v}</figcaption>
|
|
244
|
+
</figure>
|
|
245
|
+
))}
|
|
246
|
+
</div>
|
|
247
|
+
<h2 className="nv-h3">Container</h2>
|
|
248
|
+
<div style={{ display: 'grid', gap: 8 }}>
|
|
249
|
+
{(['narrow', 'content', 'standard', 'wide', 'full'] as const).map((w) => (
|
|
250
|
+
<Container key={w} width={w}>
|
|
251
|
+
<div style={{ background: 'var(--color-fg)', color: 'var(--color-bg)', fontFamily: 'var(--font-mono)', fontSize: 11, padding: '4px 8px' }}>{w}</div>
|
|
252
|
+
</Container>
|
|
253
|
+
))}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
),
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Nova 01 principles (shown on Colors). */
|
|
260
|
+
export const PRINCIPLES = F.principles
|