@deriv-com/dds-marketing 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.llm/components/awards.md +209 -0
- package/.llm/components/background.md +420 -0
- package/.llm/components/bottomsheet.md +5 -0
- package/.llm/components/comparisoncards.md +257 -0
- package/.llm/components/comparisontable.md +231 -0
- package/.llm/components/ctabanner.md +22 -0
- package/.llm/components/ctabannerslider.md +36 -2
- package/.llm/components/datatable.md +273 -0
- package/.llm/components/downloadbanner.md +283 -0
- package/.llm/components/featurecards.md +41 -3
- package/.llm/components/featurecardscarousel.md +311 -0
- package/.llm/components/featuresplit.md +5 -0
- package/.llm/components/featurestack.md +227 -0
- package/.llm/components/illustrationcard.md +236 -0
- package/.llm/components/illustrationscene.md +229 -0
- package/.llm/components/imagebanner.md +278 -0
- package/.llm/components/landinghero.md +24 -0
- package/.llm/components/navbar.md +1 -1
- package/.llm/components/navbaritem.md +4 -0
- package/.llm/components/pageheader.md +22 -0
- package/.llm/components/paginationdots.md +6 -1
- package/.llm/components/producthero.md +35 -11
- package/.llm/components/resourcebanner.md +247 -0
- package/.llm/components/scenescroll.md +222 -0
- package/.llm/components/select.md +17 -0
- package/.llm/components/stats.md +219 -0
- package/.llm/components/steps.md +286 -0
- package/.llm/components/stickycards.md +251 -0
- package/.llm/components/tableheadercell.md +19 -0
- package/.llm/components/teamgrid.md +243 -0
- package/.llm/components/testimonialrow.md +5 -0
- package/.llm/components/testimonialslider.md +14 -1
- package/.llm/components/textarea.md +13 -0
- package/.llm/components/textinput.md +13 -0
- package/.llm/components/textscroll.md +187 -0
- package/.llm/components/tiers.md +227 -0
- package/.llm/digest/awards.md +45 -0
- package/.llm/digest/background.md +53 -0
- package/.llm/digest/bottomsheet.md +2 -0
- package/.llm/digest/comparisoncards.md +51 -0
- package/.llm/digest/comparisontable.md +53 -0
- package/.llm/digest/ctabanner.md +4 -0
- package/.llm/digest/ctabannerslider.md +7 -0
- package/.llm/digest/datatable.md +65 -0
- package/.llm/digest/downloadbanner.md +65 -0
- package/.llm/digest/featurecards.md +1 -1
- package/.llm/digest/featurecardscarousel.md +54 -0
- package/.llm/digest/featuresplit.md +1 -0
- package/.llm/digest/featurestack.md +48 -0
- package/.llm/digest/illustrationcard.md +46 -0
- package/.llm/digest/illustrationscene.md +50 -0
- package/.llm/digest/imagebanner.md +64 -0
- package/.llm/digest/landinghero.md +7 -0
- package/.llm/digest/navbaritem.md +1 -0
- package/.llm/digest/pageheader.md +4 -0
- package/.llm/digest/paginationdots.md +3 -1
- package/.llm/digest/producthero.md +4 -2
- package/.llm/digest/resourcebanner.md +53 -0
- package/.llm/digest/scenescroll.md +49 -0
- package/.llm/digest/select.md +5 -0
- package/.llm/digest/stats.md +46 -0
- package/.llm/digest/steps.md +55 -0
- package/.llm/digest/stickycards.md +52 -0
- package/.llm/digest/tableheadercell.md +4 -0
- package/.llm/digest/teamgrid.md +53 -0
- package/.llm/digest/testimonialrow.md +2 -0
- package/.llm/digest/testimonialslider.md +4 -0
- package/.llm/digest/textarea.md +4 -0
- package/.llm/digest/textinput.md +4 -0
- package/.llm/digest/textscroll.md +43 -0
- package/.llm/digest/tiers.md +50 -0
- package/.llm/index.md +25 -2
- package/.llm/tokens.md +37 -1
- package/AGENTS.md +20 -1
- package/dist/components/awards-data.d.ts +24 -0
- package/dist/components/awards-data.js +11 -0
- package/dist/components/block-image-data.d.ts +24 -0
- package/dist/components/block-image-data.js +9 -0
- package/dist/components/blocks/awards/Awards.css +134 -0
- package/dist/components/blocks/awards/Awards.d.ts +24 -0
- package/dist/components/blocks/awards/Awards.js +114 -0
- package/dist/components/blocks/awards/Awards.types.d.ts +31 -0
- package/dist/components/blocks/awards/Awards.types.js +16 -0
- package/dist/components/blocks/awards/awards-impl.css +222 -0
- package/dist/components/blocks/cards-of.d.ts +16 -0
- package/dist/components/blocks/cards-of.js +23 -0
- package/dist/components/blocks/comparison-cards/ComparisonCards.css +195 -0
- package/dist/components/blocks/comparison-cards/ComparisonCards.d.ts +24 -0
- package/dist/components/blocks/comparison-cards/ComparisonCards.js +75 -0
- package/dist/components/blocks/comparison-cards/ComparisonCards.types.d.ts +49 -0
- package/dist/components/blocks/comparison-cards/ComparisonCards.types.js +26 -0
- package/dist/components/blocks/comparison-cards/comparison-cards-impl.css +262 -0
- package/dist/components/blocks/comparison-table/ComparisonTable.css +111 -0
- package/dist/components/blocks/comparison-table/ComparisonTable.d.ts +25 -0
- package/dist/components/blocks/comparison-table/ComparisonTable.js +100 -0
- package/dist/components/blocks/comparison-table/ComparisonTable.types.d.ts +36 -0
- package/dist/components/blocks/comparison-table/ComparisonTable.types.js +15 -0
- package/dist/components/blocks/comparison-table/comparison-table-impl.css +143 -0
- package/dist/components/blocks/cta-banner/CtaBanner.css +1 -0
- package/dist/components/blocks/cta-banner/CtaBanner.d.ts +1 -1
- package/dist/components/blocks/cta-banner/CtaBanner.js +3 -2
- package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +6 -0
- package/dist/components/blocks/cta-banner/CtaBanner.types.js +1 -1
- package/dist/components/blocks/cta-banner/cta-banner-impl.css +8 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.css +1 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +1 -1
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +10 -5
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +12 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.js +1 -1
- package/dist/components/blocks/cta-banner-slider/cta-banner-slider-impl.css +8 -0
- package/dist/components/blocks/data-table/DataTable.css +170 -0
- package/dist/components/blocks/data-table/DataTable.d.ts +29 -0
- package/dist/components/blocks/data-table/DataTable.js +84 -0
- package/dist/components/blocks/data-table/DataTable.types.d.ts +69 -0
- package/dist/components/blocks/data-table/DataTable.types.js +19 -0
- package/dist/components/blocks/data-table/data-table-impl.css +155 -0
- package/dist/components/blocks/download-banner/DownloadBanner.css +228 -0
- package/dist/components/blocks/download-banner/DownloadBanner.d.ts +28 -0
- package/dist/components/blocks/download-banner/DownloadBanner.js +75 -0
- package/dist/components/blocks/download-banner/DownloadBanner.types.d.ts +56 -0
- package/dist/components/blocks/download-banner/DownloadBanner.types.js +23 -0
- package/dist/components/blocks/download-banner/download-banner-impl.css +216 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.css +1 -0
- package/dist/components/blocks/faq-accordion/faq-accordion-impl.css +3 -7
- package/dist/components/blocks/feature-bullets/FeatureBullets.css +1 -0
- package/dist/components/blocks/feature-bullets/feature-bullets-impl.css +14 -1
- package/dist/components/blocks/feature-cards/FeatureCards.css +1 -0
- package/dist/components/blocks/feature-cards/FeatureCards.d.ts +2 -1
- package/dist/components/blocks/feature-cards/FeatureCards.js +4 -21
- package/dist/components/blocks/feature-cards/feature-cards-impl.css +10 -9
- package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.css +115 -0
- package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.d.ts +30 -0
- package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.js +160 -0
- package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.d.ts +52 -0
- package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.js +17 -0
- package/dist/components/blocks/feature-cards-carousel/feature-cards-carousel-impl.css +207 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.css +1 -0
- package/dist/components/blocks/feature-split/FeatureSplit.css +1 -0
- package/dist/components/blocks/feature-split/feature-split-impl.css +14 -1
- package/dist/components/blocks/feature-stack/FeatureStack.css +86 -0
- package/dist/components/blocks/feature-stack/FeatureStack.d.ts +19 -0
- package/dist/components/blocks/feature-stack/FeatureStack.js +27 -0
- package/dist/components/blocks/feature-stack/FeatureStack.types.d.ts +34 -0
- package/dist/components/blocks/feature-stack/FeatureStack.types.js +14 -0
- package/dist/components/blocks/feature-stack/feature-stack-impl.css +87 -0
- package/dist/components/blocks/image-banner/ImageBanner.css +133 -0
- package/dist/components/blocks/image-banner/ImageBanner.d.ts +32 -0
- package/dist/components/blocks/image-banner/ImageBanner.js +41 -0
- package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +54 -0
- package/dist/components/blocks/image-banner/ImageBanner.types.js +14 -0
- package/dist/components/blocks/image-banner/image-banner-impl.css +281 -0
- package/dist/components/blocks/landing-hero/LandingHero.css +1 -0
- package/dist/components/blocks/landing-hero/LandingHero.d.ts +1 -1
- package/dist/components/blocks/landing-hero/LandingHero.js +4 -3
- package/dist/components/blocks/landing-hero/LandingHero.types.d.ts +7 -3
- package/dist/components/blocks/logo-columns/LogoColumns.css +1 -0
- package/dist/components/blocks/logo-rows/LogoRows.css +1 -0
- package/dist/components/blocks/page-header/PageHeader.css +1 -0
- package/dist/components/blocks/page-header/PageHeader.d.ts +1 -1
- package/dist/components/blocks/page-header/PageHeader.js +3 -2
- package/dist/components/blocks/page-header/PageHeader.types.d.ts +6 -0
- package/dist/components/blocks/page-header/PageHeader.types.js +1 -1
- package/dist/components/blocks/page-header/page-header-impl.css +10 -0
- package/dist/components/blocks/product-hero/ProductHero.css +8 -3
- package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -1
- package/dist/components/blocks/product-hero/ProductHero.js +5 -2
- package/dist/components/blocks/product-hero/ProductHero.types.d.ts +5 -1
- package/dist/components/blocks/product-hero/ProductHero.types.js +1 -0
- package/dist/components/blocks/product-hero/product-hero-impl.css +43 -12
- package/dist/components/blocks/resource-banner/ResourceBanner.css +106 -0
- package/dist/components/blocks/resource-banner/ResourceBanner.d.ts +32 -0
- package/dist/components/blocks/resource-banner/ResourceBanner.js +51 -0
- package/dist/components/blocks/resource-banner/ResourceBanner.types.d.ts +44 -0
- package/dist/components/blocks/resource-banner/ResourceBanner.types.js +14 -0
- package/dist/components/blocks/resource-banner/resource-banner-impl.css +69 -0
- package/dist/components/blocks/scene-scroll/SceneScroll.css +93 -0
- package/dist/components/blocks/scene-scroll/SceneScroll.d.ts +36 -0
- package/dist/components/blocks/scene-scroll/SceneScroll.js +54 -0
- package/dist/components/blocks/scene-scroll/SceneScroll.types.d.ts +32 -0
- package/dist/components/blocks/scene-scroll/SceneScroll.types.js +15 -0
- package/dist/components/blocks/scene-scroll/scene-scroll-impl.css +275 -0
- package/dist/components/blocks/stats/Stats.css +139 -0
- package/dist/components/blocks/stats/Stats.d.ts +24 -0
- package/dist/components/blocks/stats/Stats.js +120 -0
- package/dist/components/blocks/stats/Stats.types.d.ts +36 -0
- package/dist/components/blocks/stats/Stats.types.js +19 -0
- package/dist/components/blocks/stats/stats-count.d.ts +34 -0
- package/dist/components/blocks/stats/stats-count.js +61 -0
- package/dist/components/blocks/stats/stats-impl.css +284 -0
- package/dist/components/blocks/stats/use-wheel.d.ts +37 -0
- package/dist/components/blocks/stats/use-wheel.js +126 -0
- package/dist/components/blocks/steps/Steps.css +202 -0
- package/dist/components/blocks/steps/Steps.d.ts +32 -0
- package/dist/components/blocks/steps/Steps.js +65 -0
- package/dist/components/blocks/steps/Steps.types.d.ts +48 -0
- package/dist/components/blocks/steps/Steps.types.js +21 -0
- package/dist/components/blocks/steps/steps-impl.css +216 -0
- package/dist/components/blocks/steps/use-turn.d.ts +33 -0
- package/dist/components/blocks/steps/use-turn.js +75 -0
- package/dist/components/blocks/sticky-cards/StickyCards.css +154 -0
- package/dist/components/blocks/sticky-cards/StickyCards.d.ts +35 -0
- package/dist/components/blocks/sticky-cards/StickyCards.js +153 -0
- package/dist/components/blocks/sticky-cards/StickyCards.types.d.ts +43 -0
- package/dist/components/blocks/sticky-cards/StickyCards.types.js +18 -0
- package/dist/components/blocks/sticky-cards/sticky-cards-impl.css +393 -0
- package/dist/components/blocks/team-grid/TeamGrid.css +159 -0
- package/dist/components/blocks/team-grid/TeamGrid.d.ts +28 -0
- package/dist/components/blocks/team-grid/TeamGrid.js +74 -0
- package/dist/components/blocks/team-grid/TeamGrid.types.d.ts +51 -0
- package/dist/components/blocks/team-grid/TeamGrid.types.js +20 -0
- package/dist/components/blocks/team-grid/team-grid-impl.css +129 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.css +1 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.d.ts +1 -1
- package/dist/components/blocks/testimonial-row/TestimonialRow.js +4 -3
- package/dist/components/blocks/testimonial-row/TestimonialRow.types.d.ts +3 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.types.js +1 -1
- package/dist/components/blocks/testimonial-row/testimonial-row-impl.css +8 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.css +1 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.d.ts +1 -1
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.js +2 -2
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.d.ts +8 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.js +1 -1
- package/dist/components/blocks/text-scroll/TextScroll.css +53 -0
- package/dist/components/blocks/text-scroll/TextScroll.d.ts +5 -0
- package/dist/components/blocks/text-scroll/TextScroll.js +71 -0
- package/dist/components/blocks/text-scroll/TextScroll.types.d.ts +22 -0
- package/dist/components/blocks/text-scroll/TextScroll.types.js +10 -0
- package/dist/components/blocks/text-scroll/text-scroll-impl.css +115 -0
- package/dist/components/blocks/tiers/Tiers.css +233 -0
- package/dist/components/blocks/tiers/Tiers.d.ts +32 -0
- package/dist/components/blocks/tiers/Tiers.js +68 -0
- package/dist/components/blocks/tiers/Tiers.types.d.ts +45 -0
- package/dist/components/blocks/tiers/Tiers.types.js +25 -0
- package/dist/components/blocks/tiers/tiers-impl.css +185 -0
- package/dist/components/blocks/use-rotation.d.ts +12 -2
- package/dist/components/blocks/use-rotation.js +6 -28
- package/dist/components/comparison-cards-data.d.ts +59 -0
- package/dist/components/comparison-cards-data.js +12 -0
- package/dist/components/comparison-table-data.d.ts +39 -0
- package/dist/components/comparison-table-data.js +11 -0
- package/dist/components/core/accordion-item/AccordionItem.css +1 -5
- package/dist/components/core/background/Background.css +92 -0
- package/dist/components/core/background/Background.d.ts +27 -0
- package/dist/components/core/background/Background.js +45 -0
- package/dist/components/core/background/Background.types.d.ts +39 -0
- package/dist/components/core/background/Background.types.js +11 -0
- package/dist/components/core/background/background-impl.css +760 -0
- package/dist/components/core/background/halftone.d.ts +134 -0
- package/dist/components/core/background/halftone.js +722 -0
- package/dist/components/core/background/patterns.d.ts +66 -0
- package/dist/components/core/background/patterns.js +244 -0
- package/dist/components/core/background/shader.d.ts +44 -0
- package/dist/components/core/background/shader.js +307 -0
- package/dist/components/core/background/use-halftone.d.ts +15 -0
- package/dist/components/core/background/use-halftone.js +27 -0
- package/dist/components/core/background/use-offscreen.d.ts +10 -0
- package/dist/components/core/background/use-offscreen.js +30 -0
- package/dist/components/core/background/use-shader.d.ts +11 -0
- package/dist/components/core/background/use-shader.js +25 -0
- package/dist/components/core/background/use-spotlight.d.ts +14 -0
- package/dist/components/core/background/use-spotlight.js +73 -0
- package/dist/components/core/bottom-sheet/BottomSheet.js +2 -2
- package/dist/components/core/bottom-sheet/BottomSheet.types.d.ts +3 -1
- package/dist/components/core/breadcrumb/Breadcrumb.d.ts +1 -0
- package/dist/components/core/breadcrumb/Breadcrumb.js +1 -0
- package/dist/components/core/breadcrumb/breadcrumb-impl.css +11 -0
- package/dist/components/core/button/button-impl.css +14 -1
- package/dist/components/core/illustration-card/IllustrationCard.css +40 -0
- package/dist/components/core/illustration-card/IllustrationCard.d.ts +22 -0
- package/dist/components/core/illustration-card/IllustrationCard.js +82 -0
- package/dist/components/core/illustration-card/IllustrationCard.types.d.ts +23 -0
- package/dist/components/core/illustration-card/IllustrationCard.types.js +8 -0
- package/dist/components/core/illustration-card/illustration-card-impl.css +47 -0
- package/dist/components/core/illustration-scene/IllustrationScene.css +41 -0
- package/dist/components/core/illustration-scene/IllustrationScene.d.ts +14 -0
- package/dist/components/core/illustration-scene/IllustrationScene.js +16 -0
- package/dist/components/core/illustration-scene/IllustrationScene.types.d.ts +32 -0
- package/dist/components/core/illustration-scene/IllustrationScene.types.js +8 -0
- package/dist/components/core/illustration-scene/illustration-scene-impl.css +46 -0
- package/dist/components/core/link/Link.js +7 -3
- package/dist/components/core/link/link-impl.css +29 -2
- package/dist/components/core/navbar/Navbar.css +2 -2
- package/dist/components/core/navbar/Navbar.types.d.ts +1 -1
- package/dist/components/core/navbar-item/NavbarItem.d.ts +2 -1
- package/dist/components/core/navbar-item/NavbarItem.js +3 -10
- package/dist/components/core/navbar-item/NavbarItem.types.d.ts +4 -1
- package/dist/components/core/navbar-item/NavbarItem.types.js +1 -1
- package/dist/components/core/navbar-item/navbar-item-impl.css +10 -0
- package/dist/components/core/navbar-menu/navbar-menu-impl.css +9 -0
- package/dist/components/core/navbar-switcher/navbar-switcher-impl.css +19 -0
- package/dist/components/core/pagination/Pagination.js +1 -1
- package/dist/components/core/pagination/pagination-impl.css +9 -0
- package/dist/components/core/pagination-dots/PaginationDots.d.ts +1 -1
- package/dist/components/core/pagination-dots/PaginationDots.js +3 -3
- package/dist/components/core/pagination-dots/PaginationDots.types.d.ts +3 -1
- package/dist/components/core/segmented-control/SegmentedControl.css +4 -3
- package/dist/components/core/segmented-control/SegmentedControl.js +17 -3
- package/dist/components/core/segmented-control-item/SegmentedControlItem.css +2 -1
- package/dist/components/core/select/Select.js +3 -3
- package/dist/components/core/select/Select.types.d.ts +11 -1
- package/dist/components/core/select/Select.types.js +1 -1
- package/dist/components/core/tab-list/TabList.js +9 -1
- package/dist/components/core/table-header-cell/TableHeaderCell.d.ts +1 -1
- package/dist/components/core/table-header-cell/TableHeaderCell.js +5 -2
- package/dist/components/core/table-header-cell/TableHeaderCell.types.d.ts +7 -1
- package/dist/components/core/table-header-cell/TableHeaderCell.types.js +1 -1
- package/dist/components/core/text-area/TextArea.d.ts +1 -1
- package/dist/components/core/text-area/TextArea.js +2 -2
- package/dist/components/core/text-area/TextArea.types.d.ts +9 -1
- package/dist/components/core/text-area/TextArea.types.js +1 -1
- package/dist/components/core/text-input/TextInput.d.ts +1 -1
- package/dist/components/core/text-input/TextInput.js +2 -2
- package/dist/components/core/text-input/TextInput.types.d.ts +9 -1
- package/dist/components/core/text-input/TextInput.types.js +1 -1
- package/dist/components/data-table-data.d.ts +33 -0
- package/dist/components/data-table-data.js +12 -0
- package/dist/components/download-banner-data.d.ts +52 -0
- package/dist/components/download-banner-data.js +11 -0
- package/dist/components/index.d.ts +51 -0
- package/dist/components/index.js +38 -0
- package/dist/components/resource-banner-data.d.ts +34 -0
- package/dist/components/resource-banner-data.js +13 -0
- package/dist/components/scene-scroll-data.d.ts +24 -0
- package/dist/components/scene-scroll-data.js +9 -0
- package/dist/components/stats-data.d.ts +28 -0
- package/dist/components/stats-data.js +11 -0
- package/dist/components/steps-data.d.ts +38 -0
- package/dist/components/steps-data.js +13 -0
- package/dist/components/sticky-cards-data.d.ts +43 -0
- package/dist/components/sticky-cards-data.js +12 -0
- package/dist/components/team-grid-data.d.ts +38 -0
- package/dist/components/team-grid-data.js +11 -0
- package/dist/components/tiers-data.d.ts +47 -0
- package/dist/components/tiers-data.js +13 -0
- package/dist/marketing.css +101 -28
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The arithmetic of a Stats figure counting up: which figures can, what one reads partway there,
|
|
3
|
+
* and the curve it climbs on. Pure, so it is tested without a browser to animate in.
|
|
4
|
+
*/
|
|
5
|
+
/** A figure that can count: the one number in it, and what is written either side. */
|
|
6
|
+
export interface Countable {
|
|
7
|
+
before: string;
|
|
8
|
+
whole: number;
|
|
9
|
+
/** How many places after the point the figure is written to — '1.2B' counts in tenths. */
|
|
10
|
+
places: number;
|
|
11
|
+
/** Whether the figure groups its thousands with commas — '1,000+' climbs as '640', '1,000'. */
|
|
12
|
+
grouped: boolean;
|
|
13
|
+
after: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The number in a figure, where it has exactly one.
|
|
17
|
+
*
|
|
18
|
+
* Exactly one, because a value with two is not a quantity: '24/7' counted up is '3/7' on the way,
|
|
19
|
+
* which is a different claim rather than a smaller one. A value with none — 'Free', 'Always' —
|
|
20
|
+
* simply shows. Everything either side of the number is kept as written, so '$' and 'M+' stand
|
|
21
|
+
* still while the digits climb.
|
|
22
|
+
*/
|
|
23
|
+
export declare function countable(value: string): Countable | null;
|
|
24
|
+
/** The figure at a share of the way to its value, written the way the value is. */
|
|
25
|
+
export declare function partway(figure: Countable, share: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* A `cubic-bezier()` easing, as a function of time.
|
|
28
|
+
*
|
|
29
|
+
* The count moves on motion.easing.reveal, the curve the figure itself rises on, so the number and
|
|
30
|
+
* the words under it settle together. A transition can name the token; a number drawn frame by
|
|
31
|
+
* frame cannot, so the curve is read off the token and walked here. Anything that is not a
|
|
32
|
+
* `cubic-bezier()` — a keyword, or nothing — counts evenly rather than on a curve invented here.
|
|
33
|
+
*/
|
|
34
|
+
export declare function easing(value: string): (time: number) => number;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The arithmetic of a Stats figure counting up: which figures can, what one reads partway there,
|
|
3
|
+
* and the curve it climbs on. Pure, so it is tested without a browser to animate in.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The number in a figure, where it has exactly one.
|
|
7
|
+
*
|
|
8
|
+
* Exactly one, because a value with two is not a quantity: '24/7' counted up is '3/7' on the way,
|
|
9
|
+
* which is a different claim rather than a smaller one. A value with none — 'Free', 'Always' —
|
|
10
|
+
* simply shows. Everything either side of the number is kept as written, so '$' and 'M+' stand
|
|
11
|
+
* still while the digits climb.
|
|
12
|
+
*/
|
|
13
|
+
export function countable(value) {
|
|
14
|
+
const match = /^(\D*)(\d{1,3}(?:,\d{3})+|\d+)(?:\.(\d+))?(\D*)$/.exec(value);
|
|
15
|
+
if (match === null)
|
|
16
|
+
return null;
|
|
17
|
+
const [, before = '', digits = '', fraction = '', after = ''] = match;
|
|
18
|
+
return {
|
|
19
|
+
before,
|
|
20
|
+
whole: Number.parseFloat(`${digits.replace(/,/g, '')}.${fraction || '0'}`),
|
|
21
|
+
places: fraction.length,
|
|
22
|
+
grouped: digits.includes(','),
|
|
23
|
+
after,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The figure at a share of the way to its value, written the way the value is. */
|
|
27
|
+
export function partway(figure, share) {
|
|
28
|
+
const [whole = '0', fraction] = (figure.whole * share).toFixed(figure.places).split('.');
|
|
29
|
+
const digits = figure.grouped ? whole.replace(/\B(?=(\d{3})+(?!\d))/g, ',') : whole;
|
|
30
|
+
return `${figure.before}${digits}${fraction === undefined ? '' : `.${fraction}`}${figure.after}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A `cubic-bezier()` easing, as a function of time.
|
|
34
|
+
*
|
|
35
|
+
* The count moves on motion.easing.reveal, the curve the figure itself rises on, so the number and
|
|
36
|
+
* the words under it settle together. A transition can name the token; a number drawn frame by
|
|
37
|
+
* frame cannot, so the curve is read off the token and walked here. Anything that is not a
|
|
38
|
+
* `cubic-bezier()` — a keyword, or nothing — counts evenly rather than on a curve invented here.
|
|
39
|
+
*/
|
|
40
|
+
export function easing(value) {
|
|
41
|
+
const points = /cubic-bezier\(([^)]*)\)/.exec(value)?.[1]?.split(',').map(Number);
|
|
42
|
+
if (points?.length !== 4 || points.some((point) => !Number.isFinite(point)))
|
|
43
|
+
return (time) => time;
|
|
44
|
+
const [x1 = 0, y1 = 0, x2 = 1, y2 = 1] = points;
|
|
45
|
+
const along = (a, b, s) => 3 * a * s * (1 - s) ** 2 + 3 * b * s * s * (1 - s) + s ** 3;
|
|
46
|
+
return (time) => {
|
|
47
|
+
// Halving for the point on the curve whose x is the time: monotonic in x, so it always lands,
|
|
48
|
+
// and twenty halvings is closer than a pixel of any figure this draws.
|
|
49
|
+
let low = 0;
|
|
50
|
+
let high = 1;
|
|
51
|
+
let s = time;
|
|
52
|
+
for (let step = 0; step < 20; step += 1) {
|
|
53
|
+
s = (low + high) / 2;
|
|
54
|
+
if (along(x1, x2, s) < time)
|
|
55
|
+
low = s;
|
|
56
|
+
else
|
|
57
|
+
high = s;
|
|
58
|
+
}
|
|
59
|
+
return along(y1, y2, s);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* A heading with no line under it closes up. Its bound step is the distance to the line, and with
|
|
7
|
+
* nothing there it would only add to the step from the header to the figures. `:last-child` is the
|
|
8
|
+
* condition itself, TestimonialRow's way, rather than a prop standing in for it.
|
|
9
|
+
*/
|
|
10
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__title:last-child {
|
|
11
|
+
margin-block-end: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The figure as written and the running number share one cell.
|
|
16
|
+
*
|
|
17
|
+
* Both in the first row and column, so the cell is as wide as the wider of the two — which is the
|
|
18
|
+
* final figure, the running number never having more digits than the one it is climbing to. That
|
|
19
|
+
* is what holds the width while it counts: the label under it and the figures beside it stay where
|
|
20
|
+
* they are. A grid area is a pair of lines rather than a keyword, so it cannot sit in the contract.
|
|
21
|
+
*/
|
|
22
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__value > * {
|
|
23
|
+
grid-area: 1 / 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* While it counts, the figure as written steps back behind the running number.
|
|
28
|
+
*
|
|
29
|
+
* Transparent rather than hidden: `visibility` and `display` would take it out of the accessibility
|
|
30
|
+
* tree, and it is the one thing there a screen reader should hear — the final figure, once. It also
|
|
31
|
+
* still holds the cell's width, which a hidden element would not.
|
|
32
|
+
*/
|
|
33
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__value[data-counting="true"] > .dds-stats__amount {
|
|
34
|
+
opacity: var(--opacity-hidden);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Two to a row on a phone. `minmax(0, 1fr)` rather than `1fr`, so a figure that will not break —
|
|
39
|
+
* a long one, at the figure's size — shrinks to its column instead of pushing the row past the page.
|
|
40
|
+
*/
|
|
41
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list {
|
|
42
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* On a phone, the wheel: one figure in place, the one before it smaller above, the next smaller
|
|
47
|
+
* below, turning on every motion.duration.hold — the reference site's mobile stats. useWheel sets
|
|
48
|
+
* `data-wheel` only below the fold, for two figures or more, and never for a reader who asked for
|
|
49
|
+
* less motion; without it the figures are the grid above.
|
|
50
|
+
*
|
|
51
|
+
* Start-aligned, as the heading is on a phone, and larger than the grid's figures — font.figure-lg
|
|
52
|
+
* for the number and body.lg for its label: one figure has the row to itself, and it is the one
|
|
53
|
+
* thing the band is showing.
|
|
54
|
+
*
|
|
55
|
+
* Every figure stands in the same cell, so the list is as tall as the tallest figure and each one
|
|
56
|
+
* is moved off it rather than laid out beside it. A step is a whole figure's own height — `100%` of
|
|
57
|
+
* the item, which the cell stretches to the tallest — rather than the arithmetic below: that sum is
|
|
58
|
+
* one line of label, and a label that wraps to two in German, or stands taller in Tamil, set every
|
|
59
|
+
* figure on the wheel into the next. The sum still sizes the room above and below. `--item` is a figure's height, the figure's line
|
|
60
|
+
* and the label's under it, so a step on the wheel is one figure: the one above sits a whole figure
|
|
61
|
+
* higher, at --dds-stats-wheel-side of its size (0.65) — the reference's scale, small enough that the one in place is plainly
|
|
62
|
+
* the one being read. The block padding is the room the side figures need, which is what is left of
|
|
63
|
+
* a figure's height once it is that far out and that much smaller.
|
|
64
|
+
*
|
|
65
|
+
* The side figures fade towards the band by a mask rather than by an ink of their own, so the wheel
|
|
66
|
+
* reads on every ground without a colour per ground.
|
|
67
|
+
*/
|
|
68
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] {
|
|
69
|
+
--dds-stats-wheel-item: calc(
|
|
70
|
+
var(--font-figure-lg-size) * var(--font-figure-lg-line-height) + var(--space-gap-sm) + var(--font-body-lg-size) *
|
|
71
|
+
var(--font-body-lg-line-height)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/* The wheel's geometry, the reference's numbers, named once. The side figures' scale; the scale a
|
|
75
|
+
figure shrinks to out of sight; the share of a figure's height the side figures need as room
|
|
76
|
+
above and below; and where the mask's fade stops, from either edge. */
|
|
77
|
+
--dds-stats-wheel-side: 0.65;
|
|
78
|
+
--dds-stats-wheel-away: 0.4;
|
|
79
|
+
--dds-stats-wheel-room: 0.85;
|
|
80
|
+
--dds-stats-wheel-fade: 35%;
|
|
81
|
+
|
|
82
|
+
grid-template-columns: minmax(0, 1fr);
|
|
83
|
+
mask-image: linear-gradient(
|
|
84
|
+
transparent,
|
|
85
|
+
black var(--dds-stats-wheel-fade),
|
|
86
|
+
black calc(100% - var(--dds-stats-wheel-fade)),
|
|
87
|
+
transparent
|
|
88
|
+
);
|
|
89
|
+
padding-block: calc(var(--dds-stats-wheel-item) * var(--dds-stats-wheel-room));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/*
|
|
93
|
+
* The wheel is a tab stop while it turns — focus holds it still — so it shows where focus is. The
|
|
94
|
+
* ring is drawn inside: the mask would fade one drawn outside at its top and foot.
|
|
95
|
+
*/
|
|
96
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel]:focus-visible {
|
|
97
|
+
outline: var(--border-width-focus) solid var(--color-border-focus);
|
|
98
|
+
outline-offset: calc(-1 * var(--border-width-focus));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:where([data-dds-context="marketing"]) .dds-stats[data-ground="strong"] .dds-stats__list[data-wheel]:focus-visible {
|
|
102
|
+
outline-color: var(--color-border-focus-on-brand-strong);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] > .dds-stats__item {
|
|
106
|
+
grid-area: 1 / 1;
|
|
107
|
+
transform-origin: left center;
|
|
108
|
+
transition-duration: var(--motion-duration-turn);
|
|
109
|
+
transition-property: opacity, scale, translate;
|
|
110
|
+
transition-timing-function: var(--motion-easing-reveal);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] .dds-stats__value {
|
|
114
|
+
font-family: var(--font-figure-lg-family);
|
|
115
|
+
font-size: var(--font-figure-lg-size);
|
|
116
|
+
font-weight: var(--font-figure-lg-weight);
|
|
117
|
+
letter-spacing: var(--font-figure-lg-letter-spacing);
|
|
118
|
+
line-height: var(--font-figure-lg-line-height);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] .dds-stats__label {
|
|
122
|
+
font-family: var(--font-body-lg-family);
|
|
123
|
+
font-size: var(--font-body-lg-size);
|
|
124
|
+
font-weight: var(--font-body-lg-weight);
|
|
125
|
+
letter-spacing: var(--font-body-lg-letter-spacing);
|
|
126
|
+
line-height: var(--font-body-lg-line-height);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] > [data-slot='above'] {
|
|
130
|
+
scale: var(--dds-stats-wheel-side);
|
|
131
|
+
translate: 0 -100%;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] > [data-slot='below'] {
|
|
135
|
+
animation: dds-stats-wheel-in var(--motion-duration-turn) var(--motion-easing-reveal);
|
|
136
|
+
scale: var(--dds-stats-wheel-side);
|
|
137
|
+
translate: 0 100%;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/*
|
|
141
|
+
* Out of sight, a figure waits where the one above leaves to — risen and shrunk away — so leaving is
|
|
142
|
+
* a movement on, never back. It comes in below by an arrival of its own, from further down and
|
|
143
|
+
* smaller, rather than by crossing the wheel to get there.
|
|
144
|
+
*/
|
|
145
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] > [data-slot='hidden'] {
|
|
146
|
+
opacity: var(--opacity-hidden);
|
|
147
|
+
scale: var(--dds-stats-wheel-away);
|
|
148
|
+
translate: 0 -100%;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@keyframes dds-stats-wheel-in {
|
|
152
|
+
from {
|
|
153
|
+
opacity: var(--opacity-hidden);
|
|
154
|
+
scale: var(--dds-stats-wheel-away);
|
|
155
|
+
translate: 0 200%;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
* A reader who asked for less motion never gets the wheel — useWheel leaves the figures a grid — so
|
|
161
|
+
* this only says so in the sheet as well: were a wheel ever drawn for them, it would change places
|
|
162
|
+
* without moving.
|
|
163
|
+
*/
|
|
164
|
+
@media (prefers-reduced-motion: reduce) {
|
|
165
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list[data-wheel] > .dds-stats__item {
|
|
166
|
+
animation: none;
|
|
167
|
+
transition: none;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__dot {
|
|
171
|
+
transition: none;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
* The wheel's dots on its start edge, and the figures beside them.
|
|
177
|
+
*
|
|
178
|
+
* While the wheel turns the section is a grid of two columns: the dots as wide as a dot, the figures
|
|
179
|
+
* the rest, the heading across both. The dots stand level with the figure in place, which is the
|
|
180
|
+
* middle of the list. Between the two, space.gap.md — the step between a control and what it
|
|
181
|
+
* describes.
|
|
182
|
+
*
|
|
183
|
+
* The dot in place is drawn long, size.dot.current, at full strength: PaginationDots' current dot
|
|
184
|
+
* on end, growing as the next figure turns in, the dots after it moving along to make room. It is
|
|
185
|
+
* the brand's coral, color.bg.brand, where the rest take the band's ink: the one mark of colour in
|
|
186
|
+
* the section says which figure is being read, and it holds on all three grounds.
|
|
187
|
+
*/
|
|
188
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__inner:has(> .dds-stats__list[data-wheel]) {
|
|
189
|
+
column-gap: var(--space-gap-md);
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__inner:has(> .dds-stats__list[data-wheel]) > .dds-stats__header {
|
|
195
|
+
grid-column: 1 / -1;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__inner:has(> .dds-stats__list[data-wheel]) > .dds-stats__dots {
|
|
199
|
+
align-self: center;
|
|
200
|
+
display: flex;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__dot {
|
|
204
|
+
transition-duration: var(--motion-duration-turn);
|
|
205
|
+
transition-property: background-color, block-size, opacity;
|
|
206
|
+
transition-timing-function: var(--motion-easing-reveal);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__dot[data-current="true"] {
|
|
210
|
+
background-color: var(--color-bg-brand);
|
|
211
|
+
block-size: var(--size-dot-current);
|
|
212
|
+
opacity: var(--opacity-dot-current);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/*
|
|
216
|
+
* The figures shrink towards the edge they start at, so the one above and the one below stay in line
|
|
217
|
+
* with the one in place and with the heading over them.
|
|
218
|
+
*/
|
|
219
|
+
:where([data-dds-context="marketing"]) .dds-stats:dir(rtl) .dds-stats__list[data-wheel] > .dds-stats__item {
|
|
220
|
+
transform-origin: right center;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
* From the fold, the header and every figure are centred, the heading over the set it names.
|
|
225
|
+
*
|
|
226
|
+
* FeatureCards' rule and its reason: centred text starts every line at a different edge, which
|
|
227
|
+
* costs little across a wide measure and a great deal across a narrow one. The header and the line
|
|
228
|
+
* under it are capped narrower than the column, so `margin-inline: auto` is what centres those
|
|
229
|
+
* boxes rather than their words inside a box left at the start edge. A figure is centred in its
|
|
230
|
+
* column, so a row of them sits under the middle of the heading rather than to its start.
|
|
231
|
+
*
|
|
232
|
+
* `48rem` longhand because `var()` is not permitted in a container query condition, and it is the
|
|
233
|
+
* width the rest of this package folds at.
|
|
234
|
+
*/
|
|
235
|
+
@container (min-width: 48rem) {
|
|
236
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__header {
|
|
237
|
+
align-items: center;
|
|
238
|
+
margin-inline: auto;
|
|
239
|
+
text-align: center;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__description {
|
|
243
|
+
margin-inline: auto;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__item {
|
|
247
|
+
align-items: center;
|
|
248
|
+
text-align: center;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__value {
|
|
252
|
+
justify-items: center;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
* How many figures share a row, read from the figures — FeatureCards' way, `:has()` asking the
|
|
257
|
+
* markup for the count it already holds rather than a prop restating it.
|
|
258
|
+
*
|
|
259
|
+
* Three to a row by default, which is five as three and two, and six as two rows of three. Three
|
|
260
|
+
* or fewer take one row of their own. Four are two rows of two here and one row at full width:
|
|
261
|
+
* four figures across a tablet's column are each narrower than a figure is wide at this size.
|
|
262
|
+
*
|
|
263
|
+
* Where `:has()` is not supported every count takes three to a row: the set still reads, only
|
|
264
|
+
* less exactly.
|
|
265
|
+
*/
|
|
266
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list {
|
|
267
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(4):last-child),
|
|
271
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(2):last-child) {
|
|
272
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :only-child) {
|
|
276
|
+
grid-template-columns: minmax(0, 1fr);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@container (min-width: 64rem) {
|
|
281
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(4):last-child) {
|
|
282
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type FocusEvent, type PointerEvent, type RefObject } from 'react';
|
|
2
|
+
/** Where a figure stands on the wheel, relative to the one in place. */
|
|
3
|
+
export type WheelSlot = 'active' | 'above' | 'below' | 'hidden';
|
|
4
|
+
/**
|
|
5
|
+
* A figure's place on the wheel at a given turn: the one in place, the one it replaced above it, the
|
|
6
|
+
* one coming in below it, and every other figure out of sight.
|
|
7
|
+
*
|
|
8
|
+
* With two figures there is no "above" — the one that leaves comes straight back in below — and with
|
|
9
|
+
* three the one above goes straight to below, which the stylesheet draws as an arrival from below
|
|
10
|
+
* rather than as a figure crossing the wheel.
|
|
11
|
+
*/
|
|
12
|
+
export declare function slotOf(at: number, turn: number, count: number): WheelSlot;
|
|
13
|
+
/**
|
|
14
|
+
* The phone's wheel of figures — the reference site's mobile stats: one figure in place, the one
|
|
15
|
+
* before it smaller above and the next smaller below, turning on to the next every
|
|
16
|
+
* motion.duration.hold plus motion.duration.turn.
|
|
17
|
+
*
|
|
18
|
+
* It turns only where every condition for it holds, and otherwise hands back `wheeling: false` and
|
|
19
|
+
* the figures are the grid they are from the fold:
|
|
20
|
+
* - the section is narrower than the fold, read off the section itself — a container, not the
|
|
21
|
+
* viewport, as the stylesheet reads it;
|
|
22
|
+
* - there are two figures or more;
|
|
23
|
+
* - the reader has not asked for less motion — the grid is the still version of the same set;
|
|
24
|
+
* - the section is on screen. A wheel nobody can see does not turn, and starts again where it was;
|
|
25
|
+
* - nothing holds or has stopped it — see `hold`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useWheel(section: RefObject<HTMLElement | null>, count: number): {
|
|
28
|
+
wheeling: boolean;
|
|
29
|
+
turn: number;
|
|
30
|
+
hold: {
|
|
31
|
+
onPointerEnter: (event: PointerEvent<HTMLElement>) => void;
|
|
32
|
+
onPointerLeave: (event: PointerEvent<HTMLElement>) => void;
|
|
33
|
+
onFocus: () => void;
|
|
34
|
+
onBlur: (event: FocusEvent<HTMLElement>) => void;
|
|
35
|
+
onPointerDownCapture: (event: PointerEvent<HTMLElement>) => void;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { milliseconds } from '../../css-time.js';
|
|
4
|
+
/**
|
|
5
|
+
* The width the figures stop turning at and lay out as a grid: the package's fold, in rem. A
|
|
6
|
+
* number here rather than a token read, because the stylesheet's container query states the same
|
|
7
|
+
* width longhand — `var()` is not permitted in one — and the two have to agree.
|
|
8
|
+
*/
|
|
9
|
+
const FOLD_REM = 48;
|
|
10
|
+
/**
|
|
11
|
+
* A figure's place on the wheel at a given turn: the one in place, the one it replaced above it, the
|
|
12
|
+
* one coming in below it, and every other figure out of sight.
|
|
13
|
+
*
|
|
14
|
+
* With two figures there is no "above" — the one that leaves comes straight back in below — and with
|
|
15
|
+
* three the one above goes straight to below, which the stylesheet draws as an arrival from below
|
|
16
|
+
* rather than as a figure crossing the wheel.
|
|
17
|
+
*/
|
|
18
|
+
export function slotOf(at, turn, count) {
|
|
19
|
+
const place = (((at - turn) % count) + count) % count;
|
|
20
|
+
if (place === 0)
|
|
21
|
+
return 'active';
|
|
22
|
+
if (place === 1)
|
|
23
|
+
return 'below';
|
|
24
|
+
if (place === count - 1)
|
|
25
|
+
return 'above';
|
|
26
|
+
return 'hidden';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The phone's wheel of figures — the reference site's mobile stats: one figure in place, the one
|
|
30
|
+
* before it smaller above and the next smaller below, turning on to the next every
|
|
31
|
+
* motion.duration.hold plus motion.duration.turn.
|
|
32
|
+
*
|
|
33
|
+
* It turns only where every condition for it holds, and otherwise hands back `wheeling: false` and
|
|
34
|
+
* the figures are the grid they are from the fold:
|
|
35
|
+
* - the section is narrower than the fold, read off the section itself — a container, not the
|
|
36
|
+
* viewport, as the stylesheet reads it;
|
|
37
|
+
* - there are two figures or more;
|
|
38
|
+
* - the reader has not asked for less motion — the grid is the still version of the same set;
|
|
39
|
+
* - the section is on screen. A wheel nobody can see does not turn, and starts again where it was;
|
|
40
|
+
* - nothing holds or has stopped it — see `hold`.
|
|
41
|
+
*/
|
|
42
|
+
export function useWheel(section, count) {
|
|
43
|
+
const [narrow, setNarrow] = useState(false);
|
|
44
|
+
const [seen, setSeen] = useState(false);
|
|
45
|
+
const [turn, setTurn] = useState(0);
|
|
46
|
+
const [still, setStill] = useState(false);
|
|
47
|
+
/*
|
|
48
|
+
A pointer over the wheel or focus on it holds it for as long as it lasts; a press anywhere on
|
|
49
|
+
it stops it for good — CtaBannerSlider's rule, by way of the WAI carousel pattern: a reader who
|
|
50
|
+
has touched the thing is reading it. Together they are the way to stop a wheel that would
|
|
51
|
+
otherwise turn for as long as it is on screen, which WCAG 2.2.2 asks of anything that moves on
|
|
52
|
+
its own for more than five seconds: the keyboard's way is the wheel's own tab stop, the
|
|
53
|
+
pointer's is resting on it, and a touch screen's is a tap.
|
|
54
|
+
*/
|
|
55
|
+
const [held, setHeld] = useState(false);
|
|
56
|
+
const [stopped, setStopped] = useState(false);
|
|
57
|
+
const wheeling = narrow && count > 1 && !still;
|
|
58
|
+
// Asked live rather than once: a reader who turns on reduced motion mid-visit stops the wheel then.
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (typeof window.matchMedia !== 'function')
|
|
61
|
+
return undefined;
|
|
62
|
+
const query = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
63
|
+
const follow = () => setStill(query.matches);
|
|
64
|
+
follow();
|
|
65
|
+
query.addEventListener?.('change', follow);
|
|
66
|
+
return () => query.removeEventListener?.('change', follow);
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const element = section.current;
|
|
70
|
+
if (element === null || typeof ResizeObserver === 'undefined')
|
|
71
|
+
return undefined;
|
|
72
|
+
const measure = () => {
|
|
73
|
+
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
74
|
+
// The layout width, not the drawn one: getBoundingClientRect reports the box after
|
|
75
|
+
// transforms, so a section shown scaled down — a thumbnail of the page, a zoomed preview —
|
|
76
|
+
// measured as a phone and turned into the wheel at desktop width. offsetWidth is the width
|
|
77
|
+
// the section is laid out at, the same one its container queries read.
|
|
78
|
+
setNarrow(element.offsetWidth < FOLD_REM * rem);
|
|
79
|
+
};
|
|
80
|
+
measure();
|
|
81
|
+
const observer = new ResizeObserver(measure);
|
|
82
|
+
observer.observe(element);
|
|
83
|
+
return () => observer.disconnect();
|
|
84
|
+
}, [section]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const element = section.current;
|
|
87
|
+
if (element === null || typeof IntersectionObserver === 'undefined')
|
|
88
|
+
return undefined;
|
|
89
|
+
const observer = new IntersectionObserver(([entry]) => setSeen(entry?.isIntersecting ?? false));
|
|
90
|
+
observer.observe(element);
|
|
91
|
+
return () => observer.disconnect();
|
|
92
|
+
}, [section]);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const element = section.current;
|
|
95
|
+
if (!wheeling || !seen || held || stopped || element === null)
|
|
96
|
+
return undefined;
|
|
97
|
+
const style = getComputedStyle(element);
|
|
98
|
+
const every = (milliseconds(style.getPropertyValue('--motion-duration-hold')) ?? 0) +
|
|
99
|
+
(milliseconds(style.getPropertyValue('--motion-duration-turn')) ?? 0);
|
|
100
|
+
if (every <= 0)
|
|
101
|
+
return undefined;
|
|
102
|
+
const timer = window.setInterval(() => setTurn((at) => at + 1), every);
|
|
103
|
+
return () => window.clearInterval(timer);
|
|
104
|
+
}, [section, wheeling, seen, held, stopped]);
|
|
105
|
+
/** Spread on the wheel's list: what holds it, and what stops it. */
|
|
106
|
+
const hold = {
|
|
107
|
+
onPointerEnter: (event) => {
|
|
108
|
+
if (event.pointerType === 'mouse')
|
|
109
|
+
setHeld(true);
|
|
110
|
+
},
|
|
111
|
+
onPointerLeave: (event) => {
|
|
112
|
+
if (event.pointerType === 'mouse')
|
|
113
|
+
setHeld(false);
|
|
114
|
+
},
|
|
115
|
+
onFocus: () => setHeld(true),
|
|
116
|
+
onBlur: (event) => {
|
|
117
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
118
|
+
setHeld(false);
|
|
119
|
+
},
|
|
120
|
+
onPointerDownCapture: (event) => {
|
|
121
|
+
if (event.isPrimary && (event.pointerType !== 'mouse' || event.button === 0))
|
|
122
|
+
setStopped(true);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
return { wheeling, turn, hold };
|
|
126
|
+
}
|