@ssaprt/tooltip 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1071 @@
1
+ Tooltip by @ssaprt
2
+
3
+ A flexible, themeable, and interactive tooltip library for React and Next.js.
4
+
5
+ Web version: <a href="https://utility-lub.vercel.app/react/UI-Components/tooltip/">Scroll to Future</a>
6
+
7
+ tooltip supports simple text hints, rich React content, interactive controls, media, custom themes, built-in animations, automatic viewport collision handling, touch interaction, and optional global presets through TooltipProvider.
8
+
9
+ The provider is optional. Every Tooltip can work independently.
10
+
11
+ Features
12
+
13
+ Standalone tooltips without a provider
14
+
15
+ Optional global TooltipProvider
16
+
17
+ More than 50 built-in theme presets
18
+
19
+ Per-tooltip theme overrides
20
+
21
+ Fully custom themes
22
+
23
+ Four preferred placements: top, bottom, left, and right
24
+
25
+ Automatic opposite-side fallback
26
+
27
+ Automatic viewport clamping
28
+
29
+ Arrow position correction near rounded corners
30
+
31
+ Unified tooltip body and arrow surface
32
+
33
+ Solid colors, gradients, borders, textures, filters, and shadows
34
+
35
+ Separate show and hide animations
36
+
37
+ Configurable animation speed and easing
38
+
39
+ Interactive tooltip content
40
+
41
+ Configurable hide delay
42
+
43
+ Text, JSX, components, forms, buttons, links, images, video, and other media
44
+
45
+ Automatic repositioning while scrolling and resizing
46
+
47
+ Automatic updates when tooltip content changes size
48
+
49
+ Mouse, touch, focus, and keyboard handling
50
+
51
+ React portal rendering into document.body
52
+
53
+ TypeScript support
54
+
55
+ React and Next.js support
56
+
57
+ Adapted for modern Chrome, Safari, Firefox, Edge, iOS Safari, and Chromium-based mobile browsers
58
+
59
+ Installation
60
+
61
+ npm install @ssaprt/tooltip
62
+
63
+ yarn add @ssaprt/tooltip
64
+
65
+ pnpm add @ssaprt/tooltip
66
+
67
+ Import the stylesheet once in the application entry point:
68
+
69
+ import "@ssaprt/tooltip/style.css";
70
+
71
+ Basic usage
72
+
73
+ TooltipProvider is not required.
74
+
75
+ "use client";
76
+
77
+ import { Tooltip } from "@ssaprt/tooltip";
78
+ import "@ssaprt/tooltip/style.css";
79
+
80
+ export const Example = () => {
81
+ return (
82
+ <Tooltip content="Copy value">
83
+ <button type="button">Copy</button>
84
+ </Tooltip>
85
+ );
86
+ };
87
+
88
+ The child element becomes the tooltip anchor.
89
+
90
+ Local configuration
91
+
92
+ Every tooltip can define its own position, theme, animation, delay, and behavior.
93
+
94
+ <Tooltip
95
+ content="Saved successfully"
96
+ position="bottom"
97
+ selectTheme="dark"
98
+ animation={{
99
+ show: "bounce",
100
+ hide: "fade",
101
+ speed: "180ms",
102
+ easing: "ease-out",
103
+ }}
104
+ hideDelay={200}
105
+ >
106
+ <button type="button">Save</button>
107
+ </Tooltip>
108
+
109
+ Tooltip inside an element
110
+
111
+ A tooltip can also be placed directly inside its target element.
112
+
113
+ <button type="button">
114
+ Delete
115
+
116
+ <Tooltip
117
+ content="Delete this item"
118
+ position="top"
119
+ selectTheme="red"
120
+ />
121
+ </button>
122
+
123
+ When children is not passed to Tooltip, the immediate parent element becomes the anchor.
124
+
125
+ Optional global provider
126
+
127
+ Use TooltipProvider when many tooltips should share the same initial position, preset theme, custom theme overrides, or animations.
128
+
129
+ "use client";
130
+
131
+ import { TooltipProvider } from "@ssaprt/tooltip";
132
+ import "@ssaprt/tooltip/style.css";
133
+
134
+ export const AppProvider = ({
135
+ children,
136
+ }: {
137
+ children: React.ReactNode;
138
+ }) => {
139
+ return (
140
+ <TooltipProvider
141
+ defaultRenderPosition="top"
142
+ selectTheme="glass"
143
+ animation={{
144
+ show: "slide",
145
+ hide: "fade",
146
+ speed: "140ms",
147
+ easing: "ease-in-out",
148
+ }}
149
+ >
150
+ {children}
151
+ </TooltipProvider>
152
+ );
153
+ };
154
+
155
+ All descendant tooltips inherit these values unless they override them locally.
156
+
157
+ <Tooltip content="Uses provider defaults">
158
+ <button type="button">Default tooltip</button>
159
+ </Tooltip>
160
+
161
+ <Tooltip
162
+ content="Uses local settings"
163
+ position="right"
164
+ selectTheme="comic"
165
+ animation={{
166
+ show: "zoom",
167
+ hide: "scale",
168
+ speed: "220ms",
169
+ }}
170
+ >
171
+ <button type="button">Local tooltip</button>
172
+ </Tooltip>
173
+
174
+ Configuration priority
175
+
176
+ Local tooltip values have priority over provider defaults.
177
+
178
+ The effective configuration is resolved in this order:
179
+
180
+ Props passed directly to Tooltip
181
+
182
+ Local customTheme
183
+
184
+ Values from TooltipProvider
185
+
186
+ Animation settings from the selected preset theme
187
+
188
+ Built-in defaults
189
+
190
+ The default built-in values are:
191
+
192
+ const defaults = {
193
+ position: "top",
194
+ selectTheme: "primary",
195
+ showAnimation: "slide",
196
+ hideAnimation: "fade",
197
+ animationSpeed: "120ms",
198
+ animationEasing: "ease-in-out",
199
+ };
200
+
201
+ Text content
202
+
203
+ <Tooltip content="Simple text tooltip">
204
+ <span>Hover me</span>
205
+ </Tooltip>
206
+
207
+ The content prop accepts any valid ReactNode.
208
+
209
+ React content
210
+
211
+ <Tooltip
212
+ content={
213
+ <div>
214
+ <strong>Build completed</strong>
215
+ <span>All files were generated successfully.</span>
216
+ </div>
217
+ }
218
+ >
219
+ <button type="button">Build status</button>
220
+ </Tooltip>
221
+
222
+ Interactive content
223
+
224
+ Set interactive when the user must be able to move the pointer into the tooltip and interact with its content.
225
+
226
+ <Tooltip
227
+ interactive
228
+ hideDelay={300}
229
+ content={
230
+ <div>
231
+ <button type="button">Previous</button>
232
+ <input type="text" placeholder="Search" />
233
+ <button type="button">Next</button>
234
+ </div>
235
+ }
236
+ >
237
+ <button type="button">Open controls</button>
238
+ </Tooltip>
239
+
240
+ With interactive enabled:
241
+
242
+ The tooltip remains visible while the pointer is inside it
243
+
244
+ Buttons, links, inputs, selects, and other controls remain usable
245
+
246
+ Moving from the anchor to the tooltip does not close it
247
+
248
+ Moving away from both the anchor and tooltip starts the hide delay
249
+
250
+ Clicking outside closes the tooltip
251
+
252
+ Pressing Escape closes the tooltip
253
+
254
+ Focus can move between the anchor and tooltip content
255
+
256
+ The default hide delay is:
257
+
258
+ 120
259
+
260
+ For interactive tooltips, the default is:
261
+
262
+ 240
263
+
264
+ A custom hideDelay overrides both values.
265
+
266
+ Rich components and media
267
+
268
+ Tooltip content is not limited to a short string. It can contain complete React interfaces, including cards, media previews, forms, navigation, images, audio, and video.
269
+
270
+ <Tooltip
271
+ interactive
272
+ position="right"
273
+ selectTheme="glass"
274
+ content={
275
+ <article
276
+ style={{
277
+ display: "grid",
278
+ width: "320px",
279
+ gap: "12px",
280
+ textAlign: "left",
281
+ }}
282
+ >
283
+ <video
284
+ controls
285
+ poster="/media/preview.jpg"
286
+ style={{
287
+ display: "block",
288
+ width: "100%",
289
+ borderRadius: "12px",
290
+ }}
291
+ >
292
+ <source
293
+ src="/media/preview.mp4"
294
+ type="video/mp4"
295
+ />
296
+ </video>
297
+
298
+ <div>
299
+ <strong>Media preview</strong>
300
+ <p>Interactive content remains available inside the tooltip.</p>
301
+ </div>
302
+
303
+ <div
304
+ style={{
305
+ display: "flex",
306
+ gap: "8px",
307
+ }}
308
+ >
309
+ <button type="button">Open</button>
310
+ <button type="button">Save</button>
311
+ </div>
312
+ </article>
313
+ }
314
+ >
315
+ <button type="button">Show preview</button>
316
+ </Tooltip>
317
+
318
+ The tooltip uses ResizeObserver, so its position is recalculated when dynamic content changes size.
319
+
320
+ Trigger elements
321
+
322
+ Wrapping a DOM element
323
+
324
+ <Tooltip content="Open settings">
325
+ <button type="button">Settings</button>
326
+ </Tooltip>
327
+
328
+ Wrapping a custom React component
329
+
330
+ The wrapped component must forward its ref to a DOM element.
331
+
332
+ import { forwardRef } from "react";
333
+
334
+ const ActionButton = forwardRef<
335
+ HTMLButtonElement,
336
+ React.ComponentProps<"button">
337
+ >((props, ref) => {
338
+ return <button ref={ref} {...props} />;
339
+ });
340
+
341
+ ActionButton.displayName = "ActionButton";
342
+
343
+ export const Example = () => {
344
+ return (
345
+ <Tooltip content="Custom component tooltip">
346
+ <ActionButton type="button">Action</ActionButton>
347
+ </Tooltip>
348
+ );
349
+ };
350
+
351
+ Using a tooltip inside a custom component
352
+
353
+ When forwarding a ref is not convenient, place Tooltip inside the final DOM element.
354
+
355
+ export const ActionButton = () => {
356
+ return (
357
+ <button type="button">
358
+ Action
359
+ <Tooltip content="Custom component tooltip" />
360
+ </button>
361
+ );
362
+ };
363
+
364
+ Placement
365
+
366
+ Available placements:
367
+
368
+ type TooltipPlacement =
369
+ | "top"
370
+ | "bottom"
371
+ | "left"
372
+ | "right";
373
+
374
+ Example:
375
+
376
+ <Tooltip content="Rendered on the left" position="left">
377
+ <button type="button">Left</button>
378
+ </Tooltip>
379
+
380
+ The requested placement is preferred, not forced.
381
+
382
+ When there is not enough space, the tooltip checks the opposite side:
383
+
384
+ top can fall back to bottom
385
+
386
+ bottom can fall back to top
387
+
388
+ left can fall back to right
389
+
390
+ right can fall back to left
391
+
392
+ The final position is clamped to the viewport so the tooltip does not render outside the visible area.
393
+
394
+ The arrow is repositioned automatically and constrained to the straight section of the edge so it does not overlap rounded corners.
395
+
396
+ Built-in themes
397
+
398
+ The package includes more than 50 ready-to-use presets with different visual styles, including clean, dark, comic, terminal, glass, neon, retro, cyberpunk, paper, metallic, natural, warning, and decorative themes.
399
+
400
+ Examples include:
401
+
402
+ "primary"
403
+ "secondary"
404
+ "dark"
405
+ "light"
406
+ "comic"
407
+ "manga"
408
+ "newspaper"
409
+ "stickyNote"
410
+ "blueprint"
411
+ "terminal"
412
+ "crt"
413
+ "pixel"
414
+ "arcade"
415
+ "cyberpunk"
416
+ "synthwave"
417
+ "vaporwave"
418
+ "hologram"
419
+ "glass"
420
+ "frost"
421
+ "clay"
422
+ "bubblegum"
423
+ "candy"
424
+ "watermelon"
425
+ "lemon"
426
+ "lava"
427
+ "ember"
428
+ "toxic"
429
+ "radioactive"
430
+ "hazard"
431
+ "policeTape"
432
+ "construction"
433
+ "parchment"
434
+ "pirateMap"
435
+ "royal"
436
+ "noir"
437
+ "detective"
438
+ "dossier"
439
+ "medical"
440
+ "laboratory"
441
+ "circuit"
442
+ "galaxy"
443
+ "aurora"
444
+ "oceanDepths"
445
+ "coralReef"
446
+ "forest"
447
+ "moss"
448
+ "desert"
449
+ "snow"
450
+ "chrome"
451
+ "goldFoil"
452
+ "bronze"
453
+ "brutalist"
454
+ "chalkboard"
455
+
456
+ Select a preset globally:
457
+
458
+ <TooltipProvider selectTheme="cyberpunk">
459
+ <App />
460
+ </TooltipProvider>
461
+
462
+ Or locally:
463
+
464
+ <Tooltip content="Local theme" selectTheme="terminal">
465
+ <button type="button">Terminal</button>
466
+ </Tooltip>
467
+
468
+ PresetsThemeType is derived from the actual preset map, so TypeScript only accepts available theme names.
469
+
470
+ Custom themes
471
+
472
+ A custom theme can be applied globally through TooltipProvider or locally through Tooltip.
473
+
474
+ <Tooltip
475
+ content="Custom theme"
476
+ customTheme={{
477
+ body: {
478
+ background:
479
+ "linear-gradient(135deg, #7c3aed, #db2777)",
480
+ filter:
481
+ "drop-shadow(0 12px 24px rgba(124, 58, 237, 0.4))",
482
+ style: {
483
+ color: "#ffffff",
484
+ padding: "12px 16px",
485
+ border: "2px solid #f0abfc",
486
+ borderRadius: "18px 6px",
487
+ fontSize: "14px",
488
+ fontWeight: 700,
489
+ },
490
+ },
491
+ arrow: {
492
+ size: "10px",
493
+ width: "24px",
494
+ },
495
+ animation: {
496
+ show: "bounce",
497
+ hide: "scale",
498
+ speed: "220ms",
499
+ easing: "cubic-bezier(0.34, 1.56, 0.64, 1)",
500
+ },
501
+ }}
502
+ >
503
+ <button type="button">Custom</button>
504
+ </Tooltip>
505
+
506
+ Custom theme values are merged with the selected preset. Only the values that must change need to be supplied.
507
+
508
+ Theme type
509
+
510
+ import type { CSSProperties } from "react";
511
+
512
+ type TooltipSize =
513
+ | `${number}px`
514
+ | `${number}rem`
515
+ | `${number}em`
516
+ | `calc(${string})`;
517
+
518
+ type ThemeType = {
519
+ body?: {
520
+ background?: CSSProperties["background"];
521
+ filter?: CSSProperties["filter"];
522
+ style?: CSSProperties;
523
+ className?: string;
524
+ };
525
+ arrow?: {
526
+ size?: TooltipSize;
527
+ width?: TooltipSize;
528
+ };
529
+ animation?: TooltipAnimationOptions;
530
+ };
531
+
532
+ body.background
533
+
534
+ Controls the complete tooltip surface background, including the arrow.
535
+
536
+ Supported values include:
537
+
538
+ Solid colors
539
+
540
+ Linear gradients
541
+
542
+ Radial gradients
543
+
544
+ Conic gradients
545
+
546
+ Repeating gradients
547
+
548
+ CSS variables
549
+
550
+ Other valid CSS background values
551
+
552
+ <Tooltip
553
+ content="Gradient"
554
+ customTheme={{
555
+ body: {
556
+ background:
557
+ "linear-gradient(135deg, #22d3ee, #7c3aed)",
558
+ },
559
+ }}
560
+ >
561
+ <button type="button">Gradient</button>
562
+ </Tooltip>
563
+
564
+ body.style
565
+
566
+ Accepts regular React CSSProperties for the tooltip content and surface configuration.
567
+
568
+ Common options include:
569
+
570
+ {
571
+ color: "#ffffff",
572
+ padding: "10px 14px",
573
+ border: "2px solid #ffffff",
574
+ borderRadius: "14px",
575
+ fontSize: "13px",
576
+ fontWeight: 600,
577
+ lineHeight: 1.4,
578
+ textAlign: "left",
579
+ maxWidth: "320px",
580
+ }
581
+
582
+ Borders and corner radii are used to generate the unified body-and-arrow surface.
583
+
584
+ body.filter
585
+
586
+ Applies a filter to the complete tooltip surface, including the arrow.
587
+
588
+ <Tooltip
589
+ content="Shadow"
590
+ customTheme={{
591
+ body: {
592
+ filter:
593
+ "drop-shadow(0 12px 18px rgba(0, 0, 0, 0.35))",
594
+ },
595
+ }}
596
+ >
597
+ <button type="button">Shadow</button>
598
+ </Tooltip>
599
+
600
+ body.className
601
+
602
+ Adds a class name to the tooltip content container.
603
+
604
+ <Tooltip
605
+ content="Class name"
606
+ customTheme={{
607
+ body: {
608
+ className: "my-tooltip-content",
609
+ },
610
+ }}
611
+ >
612
+ <button type="button">Styled</button>
613
+ </Tooltip>
614
+
615
+ arrow.size
616
+
617
+ Controls how far the arrow extends from the tooltip body.
618
+
619
+ arrow: {
620
+ size: "10px",
621
+ }
622
+
623
+ arrow.width
624
+
625
+ Controls the width of the arrow base.
626
+
627
+ arrow: {
628
+ width: "24px",
629
+ }
630
+
631
+ Animations
632
+
633
+ Show and hide animations can be configured independently.
634
+
635
+ <Tooltip
636
+ content="Animated tooltip"
637
+ animation={{
638
+ show: "flip",
639
+ hide: "blur",
640
+ speed: "200ms",
641
+ easing: "ease-out",
642
+ }}
643
+ >
644
+ <button type="button">Animate</button>
645
+ </Tooltip>
646
+
647
+ Available animation types:
648
+
649
+ type TooltipAnimationType =
650
+ | "fade"
651
+ | "slide"
652
+ | "scale"
653
+ | "zoom"
654
+ | "blur"
655
+ | "flip"
656
+ | "bounce"
657
+ | "none";
658
+
659
+ Animation options:
660
+
661
+ type TooltipAnimationSpeed =
662
+ | `${number}ms`
663
+ | `${number}s`;
664
+
665
+ type TooltipAnimationOptions = {
666
+ show?: TooltipAnimationType;
667
+ hide?: TooltipAnimationType;
668
+ speed?: TooltipAnimationSpeed;
669
+ easing?: CSSProperties["animationTimingFunction"];
670
+ };
671
+
672
+ Disable animations:
673
+
674
+ <Tooltip
675
+ content="No animation"
676
+ animation={{
677
+ show: "none",
678
+ hide: "none",
679
+ speed: "1ms",
680
+ easing: "linear",
681
+ }}
682
+ >
683
+ <button type="button">Static</button>
684
+ </Tooltip>
685
+
686
+ The stylesheet also respects prefers-reduced-motion and reduces animation duration when the user requests reduced motion.
687
+
688
+ TooltipProvider API
689
+
690
+ TooltipProvider is optional and only supplies global defaults to descendant tooltips.
691
+
692
+ Property
693
+
694
+ Type
695
+
696
+ Default
697
+
698
+ Description
699
+
700
+ children
701
+
702
+ ReactNode
703
+
704
+ Required
705
+
706
+ Components that can consume the provider defaults
707
+
708
+ defaultRenderPosition
709
+
710
+ "top" | "bottom" | "left" | "right"
711
+
712
+ "top"
713
+
714
+ Default preferred placement
715
+
716
+ selectTheme
717
+
718
+ PresetsThemeType
719
+
720
+ "primary"
721
+
722
+ Default built-in theme preset
723
+
724
+ customTheme
725
+
726
+ ThemeType
727
+
728
+ undefined
729
+
730
+ Global theme overrides merged with the selected preset
731
+
732
+ animation
733
+
734
+ TooltipAnimationOptions
735
+
736
+ Theme settings
737
+
738
+ Global show and hide animation overrides
739
+
740
+ Complete provider example:
741
+
742
+ <TooltipProvider
743
+ defaultRenderPosition="bottom"
744
+ selectTheme="glass"
745
+ customTheme={{
746
+ body: {
747
+ style: {
748
+ color: "#ffffff",
749
+ padding: "10px 14px",
750
+ borderRadius: "14px",
751
+ maxWidth: "360px",
752
+ },
753
+ },
754
+ arrow: {
755
+ size: "8px",
756
+ width: "18px",
757
+ },
758
+ }}
759
+ animation={{
760
+ show: "blur",
761
+ hide: "fade",
762
+ speed: "180ms",
763
+ easing: "ease-in-out",
764
+ }}
765
+ >
766
+ <App />
767
+ </TooltipProvider>
768
+
769
+ Tooltip API
770
+
771
+ Property
772
+
773
+ Type
774
+
775
+ Default
776
+
777
+ Description
778
+
779
+ content
780
+
781
+ ReactNode
782
+
783
+ Required
784
+
785
+ Text, JSX, components, controls, media, or any other React content
786
+
787
+ children
788
+
789
+ ReactElement
790
+
791
+ Optional
792
+
793
+ Element used as the tooltip anchor; without it, the immediate parent is used
794
+
795
+ position
796
+
797
+ TooltipPlacement
798
+
799
+ Provider value or "top"
800
+
801
+ Preferred tooltip placement
802
+
803
+ selectTheme
804
+
805
+ PresetsThemeType
806
+
807
+ Provider value or "primary"
808
+
809
+ Local built-in theme preset
810
+
811
+ customTheme
812
+
813
+ ThemeType
814
+
815
+ undefined
816
+
817
+ Local theme overrides
818
+
819
+ animation
820
+
821
+ TooltipAnimationOptions
822
+
823
+ Provider or theme settings
824
+
825
+ Local show and hide animation settings
826
+
827
+ disabled
828
+
829
+ boolean
830
+
831
+ false
832
+
833
+ Prevents the tooltip from opening
834
+
835
+ interactive
836
+
837
+ boolean
838
+
839
+ false
840
+
841
+ Enables pointer and focus interaction inside the tooltip
842
+
843
+ hideDelay
844
+
845
+ number
846
+
847
+ 120, or 240 when interactive
848
+
849
+ Delay in milliseconds before the tooltip begins hiding
850
+
851
+ Complete tooltip example:
852
+
853
+ <Tooltip
854
+ content={
855
+ <div>
856
+ <strong>Account</strong>
857
+ <button type="button">Open profile</button>
858
+ </div>
859
+ }
860
+ position="right"
861
+ selectTheme="terminal"
862
+ customTheme={{
863
+ body: {
864
+ style: {
865
+ minWidth: "220px",
866
+ textAlign: "left",
867
+ },
868
+ },
869
+ }}
870
+ animation={{
871
+ show: "zoom",
872
+ hide: "fade",
873
+ speed: "160ms",
874
+ }}
875
+ interactive
876
+ hideDelay={300}
877
+ >
878
+ <button type="button">Account</button>
879
+ </Tooltip>
880
+
881
+ Public interfaces
882
+
883
+ import type { ReactElement, ReactNode } from "react";
884
+
885
+ export type TooltipPlacement =
886
+ | "top"
887
+ | "bottom"
888
+ | "left"
889
+ | "right";
890
+
891
+ export interface TooltipProviderInterface {
892
+ defaultRenderPosition?: TooltipPlacement;
893
+ selectTheme?: PresetsThemeType;
894
+ customTheme?: ThemeType;
895
+ animation?: TooltipAnimationOptions;
896
+ }
897
+
898
+ export interface TooltipInterface {
899
+ content: ReactNode;
900
+ children?: ReactElement;
901
+ position?: TooltipPlacement;
902
+ selectTheme?: PresetsThemeType;
903
+ customTheme?: ThemeType;
904
+ animation?: TooltipAnimationOptions;
905
+ disabled?: boolean;
906
+ interactive?: boolean;
907
+ hideDelay?: number;
908
+ }
909
+
910
+ TypeScript exports
911
+
912
+ import {
913
+ Tooltip,
914
+ TooltipProvider,
915
+ } from "@ssaprt/tooltip";
916
+
917
+ import type {
918
+ PresetsThemeType,
919
+ ThemeType,
920
+ TooltipAnimationOptions,
921
+ TooltipAnimationSpeed,
922
+ TooltipAnimationType,
923
+ TooltipInterface,
924
+ TooltipPlacement,
925
+ TooltipProviderInterface,
926
+ TooltipSize,
927
+ } from "@ssaprt/tooltip";
928
+
929
+ Browser support
930
+
931
+ The package is designed for consistent behavior in modern desktop and mobile browsers:
932
+
933
+ Google Chrome
934
+
935
+ Apple Safari
936
+
937
+ Mozilla Firefox
938
+
939
+ Microsoft Edge
940
+
941
+ iOS Safari
942
+
943
+ Chromium-based Android browsers
944
+
945
+ The implementation uses browser-standard APIs and technologies:
946
+
947
+ React portals
948
+
949
+ Pointer events
950
+
951
+ Touch input
952
+
953
+ Focus events
954
+
955
+ ResizeObserver
956
+
957
+ requestAnimationFrame
958
+
959
+ SVG paths and masks
960
+
961
+ CSS custom properties
962
+
963
+ CSS gradients
964
+
965
+ CSS filters
966
+
967
+ Fixed viewport positioning
968
+
969
+ Tooltips are rendered into document.body, so they are not normally clipped by overflow: hidden or overflow: auto on application containers.
970
+
971
+ The position is recalculated when:
972
+
973
+ The window is resized
974
+
975
+ The page or an ancestor is scrolled
976
+
977
+ Tooltip content changes dimensions
978
+
979
+ Media or dynamic components change the tooltip size
980
+
981
+ Legacy Internet Explorer is not supported.
982
+
983
+ Mouse, keyboard, and touch behavior
984
+
985
+ Desktop pointer behavior:
986
+
987
+ Pointer enter opens the tooltip
988
+
989
+ Pointer leave starts the configured hide delay
990
+
991
+ Interactive content keeps the tooltip open while hovered
992
+
993
+ Keyboard behavior:
994
+
995
+ Focusing the anchor opens the tooltip
996
+
997
+ Focus can move into interactive content
998
+
999
+ Escape closes the tooltip
1000
+
1001
+ Touch behavior:
1002
+
1003
+ Tapping the anchor toggles the tooltip
1004
+
1005
+ Tapping outside closes it
1006
+
1007
+ React and Next.js
1008
+
1009
+ Tooltip and TooltipProvider use client-side browser APIs and React portals. In Next.js, render them from a Client Component.
1010
+
1011
+ "use client";
1012
+
1013
+ import { Tooltip, TooltipProvider } from "@ssaprt/tooltip";
1014
+ import "@ssaprt/tooltip/style.css";
1015
+
1016
+ export const TooltipClientProvider = ({
1017
+ children,
1018
+ }: {
1019
+ children: React.ReactNode;
1020
+ }) => {
1021
+ return (
1022
+ <TooltipProvider
1023
+ defaultRenderPosition="top"
1024
+ selectTheme="primary"
1025
+ >
1026
+ {children}
1027
+ </TooltipProvider>
1028
+ );
1029
+ };
1030
+
1031
+ Use it in a layout:
1032
+
1033
+ import { TooltipClientProvider } from "./TooltipClientProvider";
1034
+
1035
+ export default function RootLayout({
1036
+ children,
1037
+ }: {
1038
+ children: React.ReactNode;
1039
+ }) {
1040
+ return (
1041
+ <html lang="en">
1042
+ <body>
1043
+ <TooltipClientProvider>
1044
+ {children}
1045
+ </TooltipClientProvider>
1046
+ </body>
1047
+ </html>
1048
+ );
1049
+ }
1050
+
1051
+ A provider is still optional. A standalone tooltip can be rendered directly from any Client Component.
1052
+
1053
+ Rendering and overflow
1054
+
1055
+ The visible tooltip is rendered through a React portal into document.body.
1056
+
1057
+ This provides several benefits:
1058
+
1059
+ Parent overflow normally does not clip the tooltip
1060
+
1061
+ The tooltip is positioned relative to the viewport
1062
+
1063
+ Scroll and resize updates are handled globally
1064
+
1065
+ Complex content is isolated from the anchor layout
1066
+
1067
+ A high stacking level can be used without depending on the anchor's stacking context
1068
+
1069
+ License
1070
+
1071
+ MIT