Skip to content

feat(blade-svelte): Tooltip component#3364

Open
rohankokane-dev wants to merge 5 commits intomasterfrom
feat/svelte/tooltip
Open

feat(blade-svelte): Tooltip component#3364
rohankokane-dev wants to merge 5 commits intomasterfrom
feat/svelte/tooltip

Conversation

@rohankokane-dev
Copy link
Copy Markdown
Contributor

Description

Migrates the React Tooltip component to Svelte 5.

Changes

  • Adds packages/blade-svelte/src/components/Tooltip/ (Tooltip + TooltipInteractiveWrapper)
  • Adds packages/blade-core/src/styles/Tooltip/
  • Adds packages/blade-core/src/utils/getFloatingPlacementParts/ (ported helper)
  • Adds packages/blade-svelte/src/utils/portal.ts (Svelte action for portals)
  • Adds @floating-ui/dom (^1.6.0) as a runtime dep on blade-svelte
  • Re-exports Tooltip from packages/blade-svelte/src/components/index.ts
  • Re-exports CSS + utils from index files

Notes

  • Trigger DOM adds one <span class="tooltip-trigger-wrapper"> (Svelte has no cloneElement analog). aria-describedby preserves a11y.
  • useDelayGroup is deferred to a follow-up (no @floating-ui/dom equivalent).
  • box-shadow and backdrop-filter are hardcoded to React's runtime values pending --elevation-low-raised and --backdrop-blur-high token CSS vars in blade-core.

Additional Information

Artifacts (in worktree):

  • Discovery report: .cursor/artifacts/Tooltip/discovery-report.md
  • Migration plan: .cursor/artifacts/Tooltip/migration-plan.md
  • Verification report: .cursor/artifacts/Tooltip/verification-report.md
  • Screenshots: .cursor/artifacts/Tooltip/screenshots/

See the verification report for the full validation summary.

Component Checklist

  • Update Component Status Page
  • Perform Manual Testing in Other Browsers
  • Add KitchenSink Story
  • Add Interaction Tests (if applicable)
  • Add changeset

Made with Cursor

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 30, 2026

🦋 Changeset detected

Latest commit: 86575d0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@razorpay/blade-core Patch
@razorpay/blade-svelte Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

✅ PR title follows Conventional Commits specification.

@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci Bot commented Apr 30, 2026

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 86575d0:

Sandbox Source
razorpay/blade: basic Configuration

@rzpcibot
Copy link
Copy Markdown
Collaborator

rzpcibot commented Apr 30, 2026

🛡️ Coverage Report

Summary


=============================== Coverage summary ===============================
Statements   : 80.91% ( 9883/12214 )
Branches     : 67.78% ( 6094/8990 )
Functions    : 79.64% ( 2226/2795 )
Lines        : 81.88% ( 9673/11813 )
================================================================================
Full Coverage Details
---------------------------------------|---------|----------|---------|---------|-------------------
File                                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------------------------------|---------|----------|---------|---------|-------------------
All files                              |   80.91 |    67.78 |   79.64 |   81.88 |                   
 blade                                 |     100 |      100 |     100 |     100 |                   
  jestStyledComponentsSerializer.js    |     100 |      100 |     100 |     100 |                   
 ...odemods/brand-refresh/transformers |    89.1 |     89.6 |   98.85 |   88.99 |                   
  index.ts                             |   88.23 |    84.37 |     100 |   88.23 | ...15,144,179,215 
  migrate-actionlist-and-table.ts      |      75 |      100 |     100 |      75 | 23                
  migrate-amount.ts                    |   86.48 |       75 |     100 |   86.48 | 27-31,101,133,164 
  migrate-badge.ts                     |      75 |      100 |     100 |      75 | 21                
  migrate-card.ts                      |    87.5 |      100 |     100 |    87.5 | 36                
  ...te-contrast-intent-color-props.ts |   89.83 |      100 |     100 |   89.65 | ...28,153,245,291 
  migrate-divider.ts                   |   83.33 |      100 |     100 |   83.33 | 31                
  migrate-dropdown.ts                  |   91.66 |      100 |     100 |   91.66 | 72                
  migrate-typography.ts                |    90.9 |    88.13 |   96.96 |   90.82 | ...34,245,325,355 
  utils.ts                             |     100 |      100 |     100 |     100 |                   
 ...migrate-motion-tokens/transformers |    87.5 |       50 |     100 |    87.5 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  migrate-motion.ts                    |    87.5 |       50 |     100 |    87.5 | 18                
 ...s/migrate-progressbar/transformers |   78.26 |       75 |     100 |   78.26 |                   
  index.ts                             |   78.26 |       75 |     100 |   78.26 | 9,39,64-68,78     
 blade/src/components/Accordion        |    87.5 |    69.29 |   95.65 |   89.91 |                   
  Accordion.tsx                        |     100 |      100 |     100 |     100 |                   
  AccordionButton.web.tsx              |   93.33 |    68.75 |     100 |    92.3 | 41                
  AccordionContext.tsx                 |   85.71 |       50 |     100 |   85.71 | 29,42             
  AccordionItem.tsx                    |   88.23 |       88 |     100 |   88.23 | 98,112            
  AccordionItemBody.tsx                |     100 |    58.33 |     100 |     100 | 58-61             
  AccordionItemHeader.tsx              |      68 |    45.45 |      75 |   80.95 | 21-22,63,70       
  StyledAccordionButton.web.tsx        |   82.35 |    77.77 |     100 |   81.25 | 24-30             
  commonStyles.ts                      |     100 |      100 |     100 |     100 |                   
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  styles.web.ts                        |     100 |      100 |     100 |     100 |                   
 blade/src/components/ActionList       |   70.41 |    53.47 |   69.35 |   70.56 |                   
  ActionList.tsx                       |     100 |      100 |     100 |     100 |                   
  ActionListBox.web.tsx                |   67.08 |    40.47 |   73.68 |   67.08 | ...00,243,318-326 
  ActionListItem.tsx                   |   67.12 |    47.54 |      55 |   68.05 | ...19,361,392,414 
  ActionListItemAsset.web.tsx          |     100 |      100 |     100 |     100 |                   
  ActionListNoResults.tsx              |     100 |      100 |     100 |     100 |                   
  actionListUtils.ts                   |   63.63 |    50.94 |   69.23 |   63.07 | ...09,223,240,247 
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  getA11yRoles.ts                      |   82.14 |    80.95 |      80 |   82.14 | 33,48-52,81       
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/ActionList/styles |     100 |      100 |     100 |     100 |                   
  StyledListBoxWrapper.web.tsx         |     100 |      100 |     100 |     100 |                   
  getBaseListBoxWrapperStyles.ts       |     100 |      100 |     100 |     100 |                   
 blade/src/components/Alert            |   78.46 |    71.65 |     100 |   77.77 |                   
  Alert.tsx                            |   76.66 |    69.23 |     100 |   75.86 | ...76,256-258,297 
  StyledAlert.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  styles.ts                            |     100 |      100 |     100 |     100 |                   
 blade/src/components/Amount           |   88.88 |    73.78 |   81.81 |   89.74 |                   
  Amount.tsx                           |   88.31 |    73.78 |   81.81 |   89.18 | ...74,308,374-375 
  amountTokens.ts                      |     100 |      100 |     100 |     100 |                   
  index.tsx                            |       0 |        0 |       0 |       0 |                   
 ...src/components/AnimateInteractions |   76.92 |    28.57 |   55.55 |      80 |                   
  AnimateInteractions.web.tsx          |   71.42 |      100 |   33.33 |   71.42 | 43-46             
  AnimateInteractionsProvider.tsx      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useFocusWithin.ts                    |   73.33 |    16.66 |      60 |   78.57 | 17,22-23          
 blade/src/components/Avatar           |    94.2 |    81.05 |    90.9 |    94.2 |                   
  Avatar.web.tsx                       |   95.83 |    76.31 |     100 |   95.83 | 74                
  AvatarButton.tsx                     |     100 |    88.88 |     100 |     100 | 19-21             
  AvatarGroup.web.tsx                  |   92.85 |    84.61 |     100 |   92.85 | 39                
  AvatarGroupContext.tsx               |     100 |      100 |     100 |     100 |                   
  StyledAvatar.tsx                     |     100 |      100 |     100 |     100 |                   
  StyledAvatarButton.tsx               |     100 |    69.23 |     100 |     100 | 11-13,25          
  StyledAvatarGroup.tsx                |     100 |      100 |     100 |     100 |                   
  TrustedBadgeIcon.web.tsx             |   33.33 |      100 |       0 |   33.33 | 6-7               
  avatarTokens.ts                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Badge            |     100 |     91.3 |     100 |     100 |                   
  Badge.tsx                            |     100 |     91.3 |     100 |     100 | 111,147           
  StyledBadge.web.tsx                  |     100 |      100 |     100 |     100 |                   
  badgeTokens.ts                       |     100 |      100 |     100 |     100 |                   
  getStyledBadgeStyles.ts              |     100 |      100 |     100 |     100 |                   
  index.tsx                            |       0 |        0 |       0 |       0 |                   
 blade/src/components/BaseHeaderFooter |   94.11 |    78.82 |      90 |   94.11 |                   
  BaseFooter.tsx                       |     100 |      100 |     100 |     100 |                   
  BaseHeader.tsx                       |   93.75 |     77.5 |   88.88 |   93.75 | 340,383,421       
 blade/src/components/BaseMenu         |     100 |      100 |     100 |     100 |                   
  BaseMenuContext.ts                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
 ...c/components/BaseMenu/BaseMenuItem |     100 |     86.2 |     100 |     100 |                   
  BaseMenuItem.tsx                     |     100 |    84.21 |     100 |     100 | 69,103,115        
  StyledMenuItemContainer.web.tsx      |     100 |      100 |     100 |     100 |                   
  getBaseMenuItemStyles.ts             |     100 |       50 |     100 |     100 | 11                
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
 blade/src/components/BaseMotion       |   97.43 |    91.42 |     100 |   97.43 |                   
  BaseMotion.tsx                       |     100 |     91.3 |     100 |     100 | 101,127           
  baseMotionUtils.ts                   |   94.44 |    91.66 |     100 |   94.44 | 33                
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/BladeProvider    |   91.17 |       75 |   83.33 |    90.9 |                   
  BladeProvider.web.tsx                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  stylisCSSHigherSpecificity.ts        |     100 |      100 |     100 |     100 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
  useBladeProvider.ts                  |     100 |     87.5 |     100 |     100 | 32                
  useTheme.ts                          |      70 |       50 |      50 |      70 | 21,28,34          
 blade/src/components/BottomNav        |   94.44 |    78.26 |     100 |   94.44 |                   
  BottomNav.web.tsx                    |   94.44 |    78.26 |     100 |   94.44 | 76                
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/BottomSheet      |   83.33 |    75.43 |   79.72 |   83.62 |                   
  BottomSheet.web.tsx                  |   81.21 |    68.04 |   89.65 |   81.25 | ...76-382,389-396 
  BottomSheetBackdrop.web.tsx          |     100 |      100 |     100 |     100 |                   
  BottomSheetBody.web.tsx              |   94.73 |    93.75 |     100 |     100 | 35                
  BottomSheetCommon.tsx                |    87.5 |    83.33 |     100 |    87.5 | 59                
  BottomSheetContext.ts                |     100 |      100 |      25 |     100 |                   
  BottomSheetFooter.web.tsx            |     100 |      100 |     100 |     100 |                   
  BottomSheetGrabHandle.web.tsx        |     100 |      100 |     100 |     100 |                   
  BottomSheetHeader.web.tsx            |   93.33 |    94.73 |     100 |     100 | 38                
  BottomSheetStack.tsx                 |   92.85 |      100 |   71.42 |   91.66 | 17-19             
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  getBottomSheetGrabHandleStyles.ts    |     100 |       75 |     100 |     100 | 38                
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
  utils.ts                             |   16.66 |    14.28 |      50 |   16.66 | 11-25             
 blade/src/components/Box              |   96.42 |    78.57 |     100 |   96.42 |                   
  Box.tsx                              |   96.42 |    78.57 |     100 |   96.42 | 239               
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Box/BaseBox      |   94.38 |    94.31 |     100 |   94.31 |                   
  BaseBox.web.tsx                      |     100 |      100 |     100 |     100 |                   
  baseBoxStyles.ts                     |    98.3 |    96.96 |     100 |   98.27 | 308               
  getResponsiveValue.web.ts            |      90 |    92.85 |     100 |      90 | 67                
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useMemoizedStyles.web.ts             |      80 |        0 |     100 |      80 | 22-30             
 ...e/src/components/Box/BaseBox/types |     100 |      100 |     100 |     100 |                   
  propsTypes.ts                        |     100 |      100 |     100 |     100 |                   
 blade/src/components/Box/styledProps  |     100 |      100 |     100 |     100 |                   
  getStyledProps.ts                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useStyledProps.ts                    |     100 |      100 |     100 |     100 |                   
 blade/src/components/Breadcrumb       |     100 |       72 |     100 |     100 |                   
  Breadcrumb.web.tsx                   |     100 |     92.3 |     100 |     100 | 20                
  BreadcrumbContext.tsx                |     100 |      100 |     100 |     100 |                   
  BreadcrumbItem.web.tsx               |     100 |       50 |     100 |     100 | 25-53             
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Button           |       0 |        0 |       0 |       0 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Button/BaseButton |   86.27 |    86.66 |    93.1 |   88.27 |                   
  AnimatedButtonContent.web.tsx        |     100 |      100 |     100 |     100 |                   
  BaseButton.tsx                       |   83.33 |    86.82 |   90.47 |   85.59 | ...21,267,562-567 
  StyledBaseButton.web.tsx             |     100 |    76.47 |     100 |     100 | 25,45-56,70       
  buttonTokens.ts                      |     100 |      100 |     100 |     100 |                   
  getStyledBaseButtonStyles.ts         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Button/Button    |     100 |      100 |     100 |     100 |                   
  Button.tsx                           |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Button/IconButton |   88.88 |    71.42 |     100 |   88.88 |                   
  IconButton.tsx                       |     100 |      100 |     100 |     100 |                   
  StyledIconButton.web.tsx             |   83.33 |    69.23 |     100 |   83.33 | 36-40             
  index.ts                             |       0 |        0 |       0 |       0 |                   
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
 blade/src/components/ButtonGroup      |   88.46 |       72 |     100 |   88.46 |                   
  ButtonGroup.web.tsx                  |   81.25 |    66.66 |     100 |   81.25 | 31-39             
  ButtonGroupContext.tsx               |     100 |      100 |     100 |     100 |                   
  StyledButtonGroup.tsx                |     100 |       80 |     100 |     100 | 8                 
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Card             |   89.23 |    78.84 |   79.31 |   91.93 |                   
  Card.tsx                             |      92 |    85.71 |     100 |     100 | 272-302           
  CardContext.tsx                      |     100 |       75 |     100 |     100 | 9                 
  CardFooter.tsx                       |     100 |    67.74 |     100 |     100 | 61,75-91,144-164  
  CardHeader.tsx                       |   82.97 |    89.47 |      60 |   82.97 | ...71,78-80,94-96 
  CardRoot.web.tsx                     |   81.81 |    77.27 |   71.42 |   89.47 | 96-97             
  CardSurface.web.tsx                  |     100 |      100 |     100 |     100 |                   
  LinkOverlay.web.tsx                  |     100 |      100 |     100 |     100 |                   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Carousel         |   70.45 |    64.93 |    60.6 |   73.17 |                   
  Carousel.web.tsx                     |   66.22 |     66.4 |   55.17 |   69.06 | ...57,471,523-543 
  CarouselContext.tsx                  |   83.33 |       50 |     100 |   83.33 | 29                
  CarouselItem.web.tsx                 |     100 |    58.33 |     100 |     100 | 44-53,76-77       
  constants.ts                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
  utils.ts                             |     100 |      100 |     100 |     100 |                   
 ...src/components/Carousel/Indicators |   88.23 |       80 |   83.33 |   88.23 |                   
  IndicatorButton.tsx                  |     100 |      100 |     100 |     100 |                   
  Indicators.tsx                       |      75 |       50 |   66.66 |      75 | 19,33             
  StyledIndicatorButton.web.tsx        |     100 |      100 |     100 |     100 |                   
  getIndicatorButtonStyles.ts          |     100 |     87.5 |     100 |     100 | 32                
 ...mponents/Carousel/NavigationButton |   80.76 |    55.55 |      80 |   80.76 |                   
  NavigationButton.tsx                 |   61.53 |       60 |   66.66 |   61.53 | 23-28,34-37       
  StyledNavigationButton.web.tsx       |     100 |      100 |     100 |     100 |                   
  getNavigationButtonStyles.ts         |     100 |       50 |     100 |     100 | 11-70             
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Charts           |       0 |        0 |       0 |       0 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Charts/AreaChart |   91.48 |    72.97 |   88.88 |    91.3 |                   
  AreaChart.web.tsx                    |    91.3 |    72.97 |   88.88 |   91.11 | 193-197           
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Charts/BarChart  |   89.39 |    64.44 |   78.57 |   89.06 |                   
  BarChart.web.tsx                     |    87.5 |    64.44 |   76.92 |   87.27 | ...49-253,288-291 
  BarChartContext.ts                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
 ...nents/Charts/CommonChartComponents |   64.18 |    48.25 |   64.28 |   64.87 |                   
  CommonChartComponents.web.tsx        |   71.69 |    50.36 |   68.42 |   72.66 | ...74-790,819-820 
  CommonChartComponentsContext.tsx     |      60 |      100 |   33.33 |      60 | 6-9               
  index.ts                             |       0 |        0 |       0 |       0 |                   
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
  utils.ts                             |    3.22 |        0 |       0 |    3.33 | 6-53              
 ...e/src/components/Charts/DonutChart |   72.98 |     60.5 |      75 |   74.84 |                   
  DonutChart.web.tsx                   |   71.85 |     60.5 |      75 |   73.71 | ...75,513,562-565 
  index.ts                             |       0 |        0 |       0 |       0 |                   
  tokens.tsx                           |     100 |      100 |     100 |     100 |                   
 blade/src/components/Charts/LineChart |   83.33 |    80.43 |    61.9 |   82.35 |                   
  LineChart.web.tsx                    |   83.58 |    80.43 |   63.15 |   82.81 | ...07,235-239,270 
  LineChartContext.tsx                 |      75 |      100 |      50 |   66.66 | 10                
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Charts/utils     |   78.57 |    73.91 |     100 |   78.57 |                   
  getHighestColorInRange.ts            |   73.33 |    66.66 |     100 |   73.33 | 22-23,50-53       
  index.tsx                            |       0 |        0 |       0 |       0 |                   
  isSequentialColor.tsx                |      50 |       50 |     100 |      50 | 25-31             
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
  useColorTheme.tsx                    |   93.75 |    83.33 |     100 |   93.75 | 47                
 ...harts/utils/assignDataColorMapping |     100 |      100 |     100 |     100 |                   
  assignDataColorMapping.ts            |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...onents/Charts/utils/sanitizeString |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  sanitizeString.ts                    |     100 |      100 |     100 |     100 |                   
 blade/src/components/ChatInput        |   68.75 |    63.85 |   65.71 |   72.97 |                   
  ChatInput.web.tsx                    |   83.33 |    72.72 |    62.5 |    82.6 | 149,181-182,303   
  ChatInputActionBar.tsx               |     100 |      100 |     100 |     100 |                   
  ChatInputGhostSuggestion.tsx         |      80 |       75 |     100 |     100 | 20                
  chatInputTokens.ts                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useChatInput.ts                      |   61.95 |     56.6 |   60.86 |   66.23 | ...96-198,205-219 
 blade/src/components/ChatMessage      |   39.42 |    32.57 |   23.33 |   41.83 |                   
  ChatMessage.web.tsx                  |     100 |    84.84 |     100 |     100 | 32,64,70,112      
  DefaultMessageBubble.web.tsx         |     100 |     40.9 |     100 |     100 | 30-64             
  ReasoningTraces.web.tsx              |    12.5 |        0 |       0 |      14 | ...79,114,163-273 
  Rotate.web.tsx                       |     100 |      100 |     100 |     100 |                   
  SelfMessageBubble.web.tsx            |     100 |       80 |     100 |     100 | 30                
  ThumbnailPreview.web.tsx             |   26.31 |        0 |       0 |   26.31 | 23,33-37,55-106   
  token.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |     100 |      100 |     100 |     100 |                   
 blade/src/components/Checkbox         |   90.76 |    89.61 |     100 |   92.18 |                   
  Checkbox.tsx                         |     100 |    94.23 |     100 |     100 | 151,158,238       
  checkboxTokens.ts                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useCheckbox.ts                       |      80 |       80 |     100 |   82.75 | 58,72-74,112      
 .../components/Checkbox/CheckboxGroup |   85.36 |    77.14 |   86.66 |   86.48 |                   
  CheckboxGroup.tsx                    |     100 |    95.23 |     100 |     100 | 166               
  CheckboxGroupContext.ts              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useCheckboxGroup.ts                  |   76.92 |       50 |   83.33 |   77.27 | 60-64,71,79       
 ...c/components/Checkbox/CheckboxIcon |     100 |    96.42 |     100 |     100 |                   
  CheckboxIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  CheckboxIconWrapper.web.tsx          |     100 |      100 |     100 |     100 |                   
  CheckboxIconWrapperStyles.ts         |     100 |    91.66 |     100 |     100 | 31                
  Fade.web.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Chip             |   91.85 |     83.1 |     100 |   95.23 |                   
  AnimatedChip.web.tsx                 |     100 |      100 |     100 |     100 |                   
  Chip.tsx                             |   92.85 |    81.35 |     100 |     100 | ...28,158-173,228 
  ChipGroup.tsx                        |   83.33 |    91.17 |     100 |   83.33 | 59,65             
  ChipGroupContext.tsx                 |    87.5 |       50 |     100 |    87.5 | 12                
  StyledChipWrapper.web.tsx            |     100 |    88.88 |     100 |     100 | 10                
  chipTokens.ts                        |     100 |       50 |     100 |     100 | 163               
  getAnimatedChipStyles.ts             |     100 |    33.33 |     100 |     100 | 11-29             
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useChipGroup.ts                      |    87.5 |    85.71 |     100 |   88.88 | 64,75,78          
 blade/src/components/Collapsible      |   89.89 |    76.27 |   88.88 |   89.36 |                   
  Collapsible.tsx                      |   94.44 |    92.59 |     100 |   94.44 | 129               
  CollapsibleBody.tsx                  |     100 |      100 |     100 |     100 |                   
  CollapsibleBodyContent.web.tsx       |   76.47 |       50 |      70 |   76.47 | 69,109-119        
  CollapsibleButton.tsx                |     100 |       50 |     100 |     100 | 51                
  CollapsibleChevronIcon.web.tsx       |     100 |      100 |     100 |     100 |                   
  CollapsibleContext.ts                |   85.71 |       50 |     100 |   85.71 | 19                
  CollapsibleLink.tsx                  |     100 |      100 |     100 |     100 |                   
  commonStyles.ts                      |     100 |       75 |     100 |     100 | 23-24             
  componentIds.ts                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  styles.web.ts                        |     100 |      100 |     100 |     100 |                   
 blade/src/components/Counter          |     100 |    84.21 |     100 |     100 |                   
  Counter.tsx                          |     100 |    84.21 |     100 |     100 | 56-57,122         
  StyledCounter.web.tsx                |     100 |      100 |     100 |     100 |                   
  counterTokens.ts                     |     100 |      100 |     100 |     100 |                   
  getStyledCounterStyles.ts            |     100 |      100 |     100 |     100 |                   
  index.tsx                            |       0 |        0 |       0 |       0 |                   
 blade/src/components/CounterInput     |      90 |    77.63 |      96 |      95 |                   
  CounterInput.web.tsx                 |   88.88 |    77.63 |   95.65 |   94.36 | 157-162           
  CounterInputContext.tsx              |     100 |      100 |     100 |     100 |                   
  StyledCounterInput.tsx               |     100 |      100 |     100 |     100 |                   
  token.ts                             |     100 |      100 |     100 |     100 |                   
 blade/src/components/DatePicker       |    43.8 |    29.51 |   52.34 |   44.36 |                   
  BaseDatePicker.web.tsx               |   53.93 |    50.52 |   47.91 |   54.48 | ...40-443,520-554 
  Calendar.web.tsx                     |      48 |    66.66 |   15.38 |      50 | ...19-123,138-156 
  CalendarFooter.web.tsx               |    87.5 |    56.25 |     100 |     100 | 27-44             
  CalendarHeader.web.tsx               |   45.45 |     40.9 |   33.33 |   45.45 | ...09-120,172-181 
  CalendarStyles.web.tsx               |   47.05 |       55 |     100 |   45.45 | 101-148           
  DateInput.web.tsx                    |   64.63 |    35.86 |   84.61 |   65.43 | ...73,279,405-484 
  DatePicker.web.tsx                   |     100 |      100 |     100 |     100 |                   
  DatePickerContext.tsx                |     100 |      100 |     100 |     100 |                   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  shiftTimezone.tsx                    |      50 |       50 |      50 |   52.63 | 10-13,24,29-33,46 
  useControlledDates.ts                |   34.78 |       12 |     100 |   34.78 | 42-73             
  useDatesState.ts                     |    27.5 |    18.82 |   35.71 |    28.2 | ...58,163-165,189 
  usePopup.ts                          |   94.73 |    58.33 |     100 |   94.73 | 46                
  utils.ts                             |    20.1 |     8.49 |   52.94 |   19.54 | ...81-421,484-710 
 ...ts/DatePicker/FilterChipDatePicker |   23.52 |        0 |       0 |   23.52 |                   
  DatePickerFilterChip.web.tsx         |      20 |        0 |       0 |      20 | 14-37,62-67       
  FilterChipDatePicker.web.tsx         |      50 |      100 |       0 |      50 | 5                 
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/DatePicker/QuickSelection |   31.66 |       14 |   11.76 |   35.18 |                   
  PresetSideBar.web.tsx                |    7.69 |        0 |       0 |    9.09 | 16-65             
  QuickSelectionItem.web.tsx           |      50 |        0 |       0 |      50 | 14                
  renderPresetDropdown.web.tsx         |   16.66 |        0 |       0 |   16.66 | 14-29             
  usePresetState.ts                    |   45.45 |    26.92 |   33.33 |   48.38 | ...56-63,69,82-92 
  utils.ts                             |   16.66 |        0 |       0 |      25 | 8-11              
 blade/src/components/Divider          |     100 |       90 |     100 |     100 |                   
  Divider.tsx                          |     100 |       90 |     100 |     100 | 80                
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Drawer           |    92.1 |    72.72 |      76 |    92.1 |                   
  Drawer.web.tsx                       |   88.23 |    67.44 |      80 |   88.23 | 135-136,217-218   
  DrawerContext.ts                     |     100 |      100 |       0 |     100 |                   
  DrawerSubcomponents.web.tsx          |   95.45 |      100 |   83.33 |   95.45 | 49                
  StackProvider.tsx                    |   94.44 |       50 |      75 |   94.44 | 36                
  drawerComponentIds.ts                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Dropdown         |   65.53 |    57.29 |   55.33 |   67.33 |                   
  Dropdown.tsx                         |   96.72 |    78.26 |     100 |   96.66 | 136,158           
  DropdownButton.tsx                   |     100 |      100 |     100 |     100 |                   
  DropdownHeaderFooter.tsx             |   78.57 |       50 |      75 |   78.57 | 49-52             
  DropdownIconButton.tsx               |   22.22 |        0 |       0 |   22.22 | 38-70             
  DropdownLink.tsx                     |   77.77 |    83.33 |      75 |   77.77 | 79-81             
  DropdownOverlay.web.tsx              |     100 |    96.55 |     100 |     100 | 136               
  FilterChipGroup.web.tsx              |    9.09 |        0 |       0 |      10 | 17-28             
  FilterChipGroupContext.web.tsx       |     100 |      100 |   66.66 |     100 |                   
  FilterChipSelectInput.web.tsx        |   54.36 |    48.68 |      40 |    57.6 | ...89,196-207,231 
  InputDropdownButton.web.tsx          |   46.42 |     30.3 |   33.33 |      52 | 184,205-224       
  StyledDropdownOverlay.tsx            |     100 |       75 |     100 |     100 | 18                
  dropdownComponentIds.ts              |     100 |      100 |     100 |     100 |                   
  dropdownUtils.ts                     |   54.28 |    53.27 |   53.33 |   54.45 | ...73-276,292-307 
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
  useDropdown.ts                       |   69.56 |    66.66 |   58.82 |   70.78 | ...19-438,463,468 
 blade/src/components/Elevate          |     100 |      100 |     100 |     100 |                   
  Elevate.web.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/EmptyState       |     100 |      100 |     100 |     100 |                   
  EmptyState.web.tsx                   |     100 |      100 |     100 |     100 |                   
  emptyStateTokens.ts                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Fade             |     100 |       60 |     100 |     100 |                   
  Fade.web.tsx                         |     100 |       60 |     100 |     100 | 41-61             
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/FileUpload       |   51.42 |    55.65 |    37.5 |   54.87 |                   
  FileUpload.web.tsx                   |   43.28 |    51.67 |   25.71 |   47.89 | ...20-227,374-460 
  FileUploadItem.tsx                   |   69.23 |    68.75 |   42.85 |   69.23 | 110-122,150-156   
  FileUploadItemIcon.tsx               |   40.62 |    30.76 |     100 |   40.62 | 42-61,72-75       
  StyledFileUploadItemWrapper.tsx      |     100 |       75 |     100 |     100 | 18-32             
  StyledFileUploadWrapper.tsx          |     100 |      100 |     100 |     100 |                   
  fileUploadTokens.ts                  |     100 |      100 |     100 |     100 |                   
  isFileAccepted.tsx                   |   78.57 |    58.33 |     100 |   78.57 | 19,24-27          
 blade/src/components/FilterChip       |   85.71 |    63.63 |   77.77 |   85.71 |                   
  BaseFilterChip.web.tsx               |      85 |    63.63 |   77.77 |      85 | 92,145-177        
  tokens.ts                            |     100 |      100 |     100 |     100 |                   
 blade/src/components/Form             |      96 |    85.48 |     100 |      96 |                   
  FormHint.tsx                         |     100 |     90.9 |     100 |     100 | 35,114            
  FormHintWrapper.web.tsx              |     100 |      100 |     100 |     100 |                   
  FormLabel.tsx                        |   88.88 |     82.5 |     100 |   88.88 | 105,162           
  formTokens.ts                        |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  useFormId.ts                         |     100 |      100 |     100 |     100 |                   
 ...c/components/Form/CharacterCounter |     100 |      100 |     100 |     100 |                   
  CharacterCounter.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Form/Selector    |   96.87 |    71.87 |     100 |     100 |                   
  SelectorGroupField.tsx               |   88.88 |    66.66 |     100 |     100 | 33-45             
  SelectorInput.web.tsx                |     100 |      100 |     100 |     100 |                   
  SelectorLabel.web.tsx                |     100 |      100 |     100 |     100 |                   
  SelectorSupportText.tsx              |     100 |       50 |     100 |     100 | 24-29             
  SelectorTitle.tsx                    |     100 |    66.66 |     100 |     100 | 7                 
 blade/src/components/GenUI            |   73.68 |    71.73 |   68.68 |    73.2 |                   
  GenUIComponents.web.tsx              |   81.81 |     77.3 |   74.28 |    80.5 | ...8-939,968,1318 
  GenUIContext.web.tsx                 |     100 |      100 |     100 |     100 |                   
  GenUIProvider.web.tsx                |     100 |      100 |     100 |     100 |                   
  GenUISchemaRenderer.web.tsx          |    85.5 |    80.76 |   76.92 |   86.56 | ...15-216,224-231 
  rehypeAnimate.ts                     |   18.33 |    15.78 |    9.09 |   19.29 | ...0,44-63,89-119 
 blade/src/components/Icons            |       0 |        0 |       0 |       0 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
  types.ts                             |       0 |        0 |       0 |       0 |                   
 ...nts/Icons/AcceptPaymentsFilledIcon |     100 |      100 |     100 |     100 |                   
  AcceptPaymentsFilledIcon.tsx         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...omponents/Icons/AcceptPaymentsIcon |     100 |      100 |     100 |     100 |                   
  AcceptPaymentsIcon.tsx               |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/ActivityIcon |     100 |      100 |     100 |     100 |                   
  ActivityIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/AddressBookIcon |     100 |      100 |     100 |     100 |                   
  AddressBookIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ents/Icons/AffordabilityFilledIcon |     100 |      100 |     100 |     100 |                   
  AffordabilityFilledIcon.tsx          |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...components/Icons/AffordabilityIcon |     100 |      100 |     100 |     100 |                   
  AffordabilityIcon.tsx                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/AirplayIcon |     100 |      100 |     100 |     100 |                   
  AirplayIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/AlertCircleIcon |     100 |      100 |     100 |     100 |                   
  AlertCircleIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/AlertOctagonIcon |     100 |      100 |     100 |     100 |                   
  AlertOctagonIcon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/AlertOnlyIcon |     100 |      100 |     100 |     100 |                   
  AlertOnlyIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...components/Icons/AlertTriangleIcon |     100 |      100 |     100 |     100 |                   
  AlertTriangleIcon.tsx                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/AlignCenterIcon |     100 |      100 |     100 |     100 |                   
  AlignCenterIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/AlignJustifyIcon |     100 |      100 |     100 |     100 |                   
  AlignJustifyIcon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/AlignLeftIcon |     100 |      100 |     100 |     100 |                   
  AlignLeftIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...rc/components/Icons/AlignRightIcon |     100 |      100 |     100 |     100 |                   
  AlignRightIcon.tsx                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/AnchorIcon |     100 |      100 |     100 |     100 |                   
  AnchorIcon.tsx                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/AndroidIcon |     100 |      100 |     100 |     100 |                   
  AndroidIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/AnnouncementIcon |     100 |      100 |     100 |     100 |                   
  AnnouncementIcon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/ApertureIcon |     100 |      100 |     100 |     100 |                   
  ApertureIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/AppStoreIcon |     100 |      100 |     100 |     100 |                   
  AppStoreIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/AppleIcon  |     100 |      100 |     100 |     100 |                   
  AppleIcon.tsx                        |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/ArrowDownIcon |     100 |      100 |     100 |     100 |                   
  ArrowDownIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...components/Icons/ArrowDownLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowDownLeftIcon.tsx                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...omponents/Icons/ArrowDownRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowDownRightIcon.tsx               |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/ArrowLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowLeftIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...rc/components/Icons/ArrowRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowRightIcon.tsx                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/Icons/ArrowSquareDownIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareDownIcon.tsx              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ents/Icons/ArrowSquareDownLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareDownLeftIcon.tsx          |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...nts/Icons/ArrowSquareDownRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareDownRightIcon.tsx         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/Icons/ArrowSquareLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareLeftIcon.tsx              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ponents/Icons/ArrowSquareRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareRightIcon.tsx             |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...components/Icons/ArrowSquareUpIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareUpIcon.tsx                |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...onents/Icons/ArrowSquareUpLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareUpLeftIcon.tsx            |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...nents/Icons/ArrowSquareUpRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowSquareUpRightIcon.tsx           |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/ArrowUpIcon |     100 |      100 |     100 |     100 |                   
  ArrowUpIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/ArrowUpLeftIcon |     100 |      100 |     100 |     100 |                   
  ArrowUpLeftIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/ArrowUpRightIcon |     100 |      100 |     100 |     100 |                   
  ArrowUpRightIcon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/AtSignIcon |     100 |      100 |     100 |     100 |                   
  AtSignIcon.tsx                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...rc/components/Icons/AttachmentIcon |     100 |      100 |     100 |     100 |                   
  AttachmentIcon.tsx                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...nents/Icons/AutomateAccountingIcon |     100 |      100 |     100 |     100 |                   
  AutomateAccountingIcon.tsx           |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ts/Icons/AutomatePayrollFilledIcon |     100 |      100 |     100 |     100 |                   
  AutomatePayrollFilledIcon.tsx        |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/Icons/AutomatePayrollIcon |     100 |      100 |     100 |     100 |                   
  AutomatePayrollIcon.tsx              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/AwardIcon  |     100 |      100 |     100 |     100 |                   
  AwardIcon.tsx                        |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../BankAccountVerificationFilledIcon |     100 |      100 |     100 |     100 |                   
  ...AccountVerificationFilledIcon.tsx |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../Icons/BankAccountVerificationIcon |     100 |      100 |     100 |     100 |                   
  BankAccountVerificationIcon.tsx      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BankIcon   |     100 |      100 |     100 |     100 |                   
  BankIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/BarChartAltIcon |     100 |      100 |     100 |     100 |                   
  BarChartAltIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/BarChartIcon |     100 |      100 |     100 |     100 |                   
  BarChartIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/BarCodeIcon |     100 |      100 |     100 |     100 |                   
  BarCodeIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...onents/Icons/Battery100PercentIcon |     100 |      100 |     100 |     100 |                   
  Battery100PercentIcon.tsx            |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ponents/Icons/Battery20PercentIcon |     100 |      100 |     100 |     100 |                   
  Battery20PercentIcon.tsx             |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ponents/Icons/Battery40PercentIcon |     100 |      100 |     100 |     100 |                   
  Battery40PercentIcon.tsx             |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ponents/Icons/Battery60PercentIcon |     100 |      100 |     100 |     100 |                   
  Battery60PercentIcon.tsx             |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...ponents/Icons/Battery80PercentIcon |     100 |      100 |     100 |     100 |                   
  Battery80PercentIcon.tsx             |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/Icons/BatteryChargingIcon |     100 |      100 |     100 |     100 |                   
  BatteryChargingIcon.tsx              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/BatteryIcon |     100 |      100 |     100 |     100 |                   
  BatteryIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BellIcon   |     100 |      100 |     100 |     100 |                   
  BellIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...e/src/components/Icons/BellOffIcon |     100 |      100 |     100 |     100 |                   
  BellOffIcon.tsx                      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...rc/components/Icons/BfsiFilledIcon |     100 |      100 |     100 |     100 |                   
  BfsiFilledIcon.tsx                   |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BfsiIcon   |     100 |      100 |     100 |     100 |                   
  BfsiIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BillIcon   |     100 |      100 |     100 |     100 |                   
  BillIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/BillMeFilledIcon |     100 |      100 |     100 |     100 |                   
  BillMeFilledIcon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BillMeIcon |     100 |      100 |     100 |     100 |                   
  BillMeIcon.tsx                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/BluetoothIcon |     100 |      100 |     100 |     100 |                   
  BluetoothIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BoldIcon   |     100 |      100 |     100 |     100 |                   
  BoldIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BookIcon   |     100 |      100 |     100 |     100 |                   
  BookIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/BookmarkIcon |     100 |      100 |     100 |     100 |                   
  BookmarkIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BoxIcon    |     100 |      100 |     100 |     100 |                   
  BoxIcon.tsx                          |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/BriefcaseIcon |     100 |      100 |     100 |     100 |                   
  BriefcaseIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/BugIcon    |     100 |      100 |     100 |     100 |                   
  BugIcon.tsx                          |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/BuildingIcon |     100 |      100 |     100 |     100 |                   
  BuildingIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...onents/Icons/BulkPayoutsFilledIcon |     100 |      100 |     100 |     100 |                   
  BulkPayoutsFilledIcon.tsx            |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/BulkPayoutsIcon |     100 |      100 |     100 |     100 |                   
  BulkPayoutsIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...mponents/Icons/BusinessBankingIcon |     100 |      100 |     100 |     100 |                   
  BusinessBankingIcon.tsx              |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../BusinessSpendManagementFilledIcon |     100 |      100 |     100 |     100 |                   
  ...nessSpendManagementFilledIcon.tsx |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../Icons/BusinessSpendManagementIcon |     100 |      100 |     100 |     100 |                   
  BusinessSpendManagementIcon.tsx      |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../src/components/Icons/CalendarIcon |     100 |      100 |     100 |     100 |                   
  CalendarIcon.tsx                     |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/CameraIcon |     100 |      100 |     100 |     100 |                   
  CameraIcon.tsx                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...src/components/Icons/CameraOffIcon |     100 |      100 |     100 |     100 |                   
  CameraOffIcon.tsx                    |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/CashIcon   |     100 |      100 |     100 |     100 |                   
  CashIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/CastIcon   |     100 |      100 |     100 |     100 |                   
  CastIcon.tsx                         |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 .../components/Icons/CheckCircle2Icon |     100 |      100 |     100 |     100 |                   
  CheckCircle2Icon.tsx                 |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 ...c/components/Icons/CheckCircleIcon |     100 |      100 |     100 |     100 |                   
  CheckCircleIcon.tsx                  |     100 |      100 |     100 |     100 |                   
  index.ts                             |       0 |        0 |       0 |       0 |                   
 blade/src/components/Icons/CheckIcon  |     100 |      100 |     100 |     100 |                   
  CheckIcon.tsx     ...*[Comment body truncated]*

<p class={tooltipTitleClass}>{title}</p>
{/if}
<p class={tooltipContentClass}>{content}</p>
<svg
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Arrow SVG viewBox height is wrong

The viewBox uses ARROW_WIDTH (14) for both width and height, but ARROW_HEIGHT is 8. This means the arrow SVG has a square 14x14 viewBox while the path only draws within a 14x8 area. The extra vertical space may cause subtle positioning misalignment because floating-ui's arrow middleware measures the actual SVG element dimensions.

Suggested change
<svg
width={ARROW_WIDTH}
height={ARROW_HEIGHT}
viewBox="0 0 {ARROW_WIDTH} {ARROW_HEIGHT}"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86575d0. Updated height and viewBox to use ARROW_HEIGHT (8) so the SVG element matches the actual path bounds; floating-ui's arrow middleware now measures the correct dimensions.

let resolvedPlacement = $state<TooltipPlacement>(placement);
let floatingX = $state(0);
let floatingY = $state(0);
let arrowX = $state<number | null>(null);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: tooltipId uses Math.random() -- not SSR-safe

If blade-svelte ever supports SSR, Math.random() will produce different IDs on server vs client, causing a hydration mismatch for the aria-describedby / id pair. Consider using a deterministic counter or Svelte's $props.id pattern if available.

Not urgent since this is client-only today, but worth noting for future-proofing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking — not blocking for the current client-only blade-svelte. Will switch to a module-level counter (same pattern AccordionItem now uses in #3348) when SSR support lands.

/* Mount the bubble as soon as it should open and keep it mounted until the
* close transition finishes. We toggle `data-state` so CSS handles the
* fade/slide; `isMounted` controls whether the DOM node exists at all. */
$effect(() => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential issue: handleBubbleTransitionEnd may never fire

If CSS transitions are disabled (e.g. prefers-reduced-motion: reduce), interrupted, or if the duration token --duration-quick resolves to 0s, the transitionend event will never fire and isMounted stays true forever. This means the portal DOM node leaks until the component is destroyed.

Consider adding a fallback timeout, or checking for reduced motion and setting isMounted = false directly in the $effect when isOpen becomes false:

$effect(() => {
  if (!isOpen) {
    dataState = 'closed';
    // Fallback: if transition doesn't fire within a reasonable window, unmount
    const fallback = setTimeout(() => { isMounted = false; }, 300);
    return () => clearTimeout(fallback);
  }
  // ...existing open logic...
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86575d0. Added a 240ms fallback setTimeout in the close-state effect that unmounts the bubble if transitionend doesn't fire (prefers-reduced-motion, --duration-quick: 0s, or interrupted transitions). Cleared on re-open or when the listener wins the race so we don't double-unmount.

let arrowX = $state<number | null>(null);
let arrowY = $state<number | null>(null);

const tooltipId = `blade-tooltip-${Math.random().toString(36).slice(2, 10)}`;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf nit: getFloatingPlacementParts called twice in isHorizontal

This derived value calls getFloatingPlacementParts(placement) twice -- once for the left check and once for right. Since it splits a string and allocates a tuple each time, consider extracting once:

const placementSide = $derived(getFloatingPlacementParts(placement)[0]);
const isHorizontal = $derived(placementSide === 'left' || placementSide === 'right');

This also simplifies readability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking — out of scope for this must-fix pass. Will extract getFloatingPlacementParts(placement) into a single $derived in a follow-up.

);

/* Rotate the base ▼ path so the apex points at the trigger; anchor with
* `[side]: 100%` so the base stays flush with the bubble edge on all sides. */
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: metaAttrs is not reactive to testID changes

metaAttrs is computed once at component init (plain const, not $derived), so if testID is changed dynamically by the parent, the meta attribute on the portal will be stale. Other components in this codebase (Badge, Alert) also do this at init time, so it may be an intentional pattern -- but worth calling out since the tooltip portal lives in document.body and might be harder to debug when test selectors don't update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Matches the existing pattern across blade-svelte components (Badge, Alert, etc.); will revisit holistically rather than per-component.

): PortalActionReturn {
let resolved = resolveTarget(target);
resolved.appendChild(node);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Portal action does not save the original parent

When update() is called with a new target, the node is moved but there is no record of where it originally was. The destroy() just removes the node from whatever parent it is in. This is fine for the current Tooltip use case (always targets document.body, never updates), but the action is exported as a general-purpose utility from @razorpay/blade-svelte/utils.

If any future consumer relies on destroy() to restore the node to its original position (the common expectation for portal actions), they will be surprised. Consider either:

  1. Saving the original parent and restoring on destroy
  2. Using a placeholder Comment node pattern (common in Svelte portal implementations)
  3. Or at minimum, documenting that destroy() removes the node entirely rather than restoring it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. The action is currently used only by Tooltip (always portals to document.body, never updates) so the simpler 'remove on destroy' semantics work. Will revisit when a second consumer needs restore-to-original-parent behavior.

*/
.bubble[data-state='closed'] {
opacity: 0;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS: dark mode border selector relies on ancestor [data-color-scheme]

The selector [data-color-scheme='dark'] .bubble requires .bubble to be a descendant of [data-color-scheme]. However, the tooltip bubble is portaled to document.body. This means the dark-mode border will only apply if <body> or an ancestor of <body> has data-color-scheme="dark".

Can you confirm that BladeProvider sets data-color-scheme on a common ancestor that remains above the portal target? If it sets it on a wrapper div inside body rather than on <html> or <body> itself, these dark-mode styles will never match.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86575d0. Verified that blade-svelte sets body[data-theme='dark'] (preview.js) which is what @razorpay/blade-core/tokens/theme.css uses (:root body[data-theme='dark']). Updated both selectors in tooltip.module.css from [data-color-scheme='dark'] to body[data-theme='dark'] so the dark-mode bubble border + arrow stroke actually apply when the bubble is portaled to document.body.

@@ -0,0 +1,49 @@
/**
* Svelte action that moves a node into a target element (defaults to
* `document.body`) for the lifetime of the action, then restores it on destroy.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSDoc says "restores it on destroy" but it does not

The doc comment at line 3 says the action "moves a node into a target element...then restores it on destroy." But destroy() calls removeChild() -- it removes the node from the DOM entirely, it does not restore it to its original parent. The doc should be updated to match the actual behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking — out of scope for this must-fix pass. Will update the JSDoc (or implement actual restore semantics) in a follow-up.

Copy link
Copy Markdown
Contributor Author

@rohankokane-dev rohankokane-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tooltip Svelte 5 Migration -- Review Summary

Overall this is a well-structured migration. The component follows established blade-svelte patterns (CVA in blade-core, $props() destructuring, $derived/$effect usage, meta/analytics attrs). The floating-ui integration is solid, the arrow math is correctly ported, and the stories provide good coverage. A few items to address:

Must Fix

  1. Arrow SVG viewBox bug (Tooltip.svelte L249): height and viewBox both use ARROW_WIDTH (14) instead of ARROW_HEIGHT (8). This creates a 14x14 SVG when the path only occupies a 14x8 area, which will cause floating-ui's arrow middleware to miscalculate positioning since it measures the actual DOM element.

  2. transitionend may never fire (Tooltip.svelte L121): If prefers-reduced-motion: reduce is active, or --duration-quick resolves to 0s, the transitionend event never fires and isMounted stays true. The portal DOM node leaks. Add a fallback timeout.

  3. Dark mode CSS selector + portal (tooltip.module.css L84): [data-color-scheme='dark'] .bubble requires .bubble to be a descendant of the scheme attribute. Since the bubble is portaled to document.body, verify that BladeProvider sets data-color-scheme on <html> or <body> -- not on a wrapper div below body.

Should Fix

  1. Portal JSDoc is inaccurate (portal.ts L3): Says "restores it on destroy" but destroy() removes the node from the DOM entirely. Update the doc or implement restore-to-original-parent behavior.

  2. Duplicate getFloatingPlacementParts calls (Tooltip.svelte L87): Called twice to check left/right. Extract to a single $derived for clarity and to avoid redundant string splitting.

Nits / Future Considerations

  1. Math.random() for tooltipId (L84): Not SSR-safe. Fine for now since blade-svelte is client-only, but consider a counter-based approach for future-proofing.

  2. metaAttrs not reactive (L197): Computed once at init, not via $derived. Matches other blade-svelte components, so likely intentional, but worth documenting.

  3. Portal action design (portal.ts L37): Exported as a general utility but doesn't preserve the original parent. Document the "remove on destroy" semantics or add placeholder-based restore logic.

What Looks Good

  • Clean separation: blade-core owns CSS/CVA, blade-svelte owns the component logic
  • Proper use of $effect with cleanup return for autoUpdate and requestAnimationFrame
  • TooltipInteractiveWrapper with context-based nesting assertion
  • aria-describedby + role="tooltip" accessibility pattern
  • Comprehensive Storybook coverage (placement grid, interactive wrapper, custom triggers)
  • Good inline documentation of React parity decisions and TODO tracking for hardcoded tokens

rohankokane and others added 2 commits May 6, 2026 10:52
- Arrow SVG `viewBox` and `height` were using `ARROW_WIDTH` (14) for both
  axes, producing a 14x14 square viewBox while the path only draws a
  14x8 arrow. floating-ui's arrow middleware measures the actual SVG
  element, so this caused subtle positioning misalignment. Switched to
  `ARROW_HEIGHT` for both `height` and the viewBox y-extent.
- Bubble unmount could leak the portal DOM node when CSS transitions
  were skipped (`prefers-reduced-motion: reduce`, `--duration-quick`
  resolving to `0s`, or interrupted transitions) because `transitionend`
  never fired. Added a 240ms fallback timeout that unmounts if the
  transition listener doesn't fire first.
- Dark-mode CSS used `[data-color-scheme='dark']` but blade-core/blade-
  svelte both set `body[data-theme='dark']`. Selector swapped so dark-
  mode bubble border + arrow stroke now actually apply.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants