Skip to main content

_Tooltip.cshtml โ€” Reusable Tooltip Partial

Location: Views/Shared/UI/_Tooltip.cshtml

A self-contained hover tooltip for ASP.NET Core Razor views. Supports rich HTML content, multiple placements, RTL/LTR layouts, and auto viewport-aware flipping.


Visual Previewโ€‹

alt text alt text


How It Worksโ€‹

  • Trigger โ€” an inline element (<span>) containing an optional text label and/or an icon.
  • Bubble โ€” appears on :hover or :focus-within of the trigger.
  • Smart positioning โ€” on hover the JS measures viewport space and automatically flips the bubble to the opposite side if there is not enough room.
  • Horizontal shift โ€” if the bubble would overflow the left/right viewport edge it is shifted in-place.
  • Shared assets โ€” the &lt;style> and &lt;script> blocks are injected only once per page regardless of how many tooltips are on the page (tracked via ViewData).

Propsโ€‹

PropTypeDefaultDescription
labelstring(empty)Trigger label text. HTML is allowed (e.g. <span class='text-danger'>*</span>). Omit for icon-only triggers.
contentstring(empty)Tooltip body. Accepts any HTML โ€” plain text, <ul>, <ol>, <strong>, links, etc. Required.
iconstring(empty)Full HTML for the trigger icon, e.g. <i class="bi bi-info-circle"></i>. At least one of label or icon must be provided.
placementstring"top"Preferred bubble direction: "top" ยท "bottom" ยท "start" ยท "end". The JS may flip this at runtime based on available space.
maxHeightstring"500px"Maximum height of the bubble before it becomes scrollable. Any valid CSS length (px, rem, vh).
cssClassstring(empty)Extra CSS class(es) appended to the outer wrapper <span>.
idstring(auto)Stable id suffix used to build tip-&#123;id&#125;. Auto-generated (Guid) when omitted. Provide a value when you need a predictable DOM id (e.g. for testing or ARIA).

[!NOTE] The partial renders nothing if content is empty, or if both label and icon are empty.


Basic Usageโ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
label = "ุงู‚ุฑุฃ ุงู„ู…ุฒูŠุฏ",
icon = "<i class=\"bi bi-question-circle-fill\"></i>",
content = "ู‡ุฐุง ู†ุต ุชู„ู…ูŠุญ ุจุณูŠุท ูŠุดุฑุญ ุงู„ุนู†ุตุฑ."
})

Examplesโ€‹

1 โ€” Label + Icon, top placement (default)โ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
label = "ู…ุฐูƒุฑุฉ ุชูˆุถูŠุญูŠุฉ <span class='text-danger'>*</span>",
icon = "<i class=\"bi bi-question-circle-fill text-secondary\"></i>",
content = @"<ol>
<li>ุจูŠุงู† ุงู„ุณู†ุฏ ุงู„ู†ุธุงู…ูŠ ู„ู„ุงุฎุชุตุงุต.</li>
<li>ุงู„ู‡ุฏู ู…ู† ุงู„ู…ุดุฑูˆุน ุงู„ู…ู‚ุชุฑุญ.</li>
<li>ุงู„ุนู†ุงุตุฑ ุงู„ุฑุฆูŠุณูŠุฉ ู„ู„ู…ุดุฑูˆุน.</li>
</ol>"
})

2 โ€” Icon-only triggerโ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
icon = "<i class=\"bi bi-info-circle text-primary fs-5\"></i>",
content = "ูŠู…ูƒู†ูƒ ุฑูุน ู…ู„ูุงุช PDF ุฃูˆ Word ูู‚ุท."
})

3 โ€” Bottom placement with plain textโ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
label = "ุฑุณูˆู… ุงู„ุฎุฏู…ุฉ",
icon = "<i class=\"bi bi-currency-dollar\"></i>",
placement = "bottom",
content = "ุงู„ุฑุณูˆู… ุบูŠุฑ ู‚ุงุจู„ุฉ ู„ู„ุงุณุชุฑุฏุงุฏ ุจุนุฏ ุชู‚ุฏูŠู… ุงู„ุทู„ุจ."
})

4 โ€” Side placement (start / end) with custom heightโ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
label = "ู…ุชุทู„ุจุงุช ุงู„ู…ุณุชู†ุฏ",
icon = "<i class=\"bi bi-paperclip\"></i>",
placement = "start",
maxHeight = "200px",
content = @"<ul>
<li>ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ ุณุงุฑูŠุฉ ุงู„ู…ูุนูˆู„</li>
<li>ุนู‚ุฏ ุงู„ุนู…ู„ ู…ูˆุซู‚</li>
<li>ูƒุดู ุงู„ุฑุงุชุจ ู„ุขุฎุฑ 3 ุฃุดู‡ุฑ</li>
</ul>"
})

5 โ€” Stable id (for testing / ARIA references)โ€‹

@await Html.PartialAsync("~/Views/Shared/UI/_Tooltip.cshtml", new {
id = "docs-tip",
label = "ูˆุซุงุฆู‚ ู…ุทู„ูˆุจุฉ",
content = "ุงู„ูˆุซูŠู‚ุฉ ุงู„ุฑุณู…ูŠุฉ ู…ุทู„ูˆุจุฉ ู„ู„ู…ุฑุงุฌุนุฉ."
})

Rendered DOM id will be tip-docs-tip.


CSS Helper Classesโ€‹

These classes are available inside tooltip content and on the wrapper:

ClassWhere to useEffect
tt-highlightInside content HTMLYellow highlight badge โ€” <span class="tt-highlight">keyword</span>
tt-label-dashedAdd to wrapper via cssClassAdds a dashed underline to the label text

Highlight example:

content = "ูŠุฌุจ ุฃู† ุชูƒูˆู† <span class='tt-highlight'>ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ</span> ุณุงุฑูŠุฉ ุงู„ู…ูุนูˆู„."

Placement Referenceโ€‹

[top] โ† default
โ†‘
[end] โ† TRIGGER โ†’ [start]
โ†“
[bottom]

[!IMPORTANT] start and end are logical (RTL-aware), not physical:

  • In RTL pages: start = right side, end = left side.
  • In LTR pages: start = left side, end = right side.

The JS will automatically flip top โ†” bottom or start โ†” end when the preferred side lacks viewport space.


Scrollable Contentโ€‹

When the content height exceeds maxHeight, the bubble becomes vertically scrollable automatically. A thin, styled scrollbar appears on overflow. Set maxHeight to control the threshold:

maxHeight = "300px" @* default is 500px *@

Accessibilityโ€‹

  • The wrapper renders with tabindex="0" and role="button" โ€” keyboard users can focus it with Tab.
  • The bubble carries role="tooltip" and is linked via aria-describedby to the trigger.
  • dir="auto" on the bubble ensures correct bidirectional text rendering inside mixed-language content.

Notes & Gotchasโ€‹

[!WARNING] The partial renders nothing silently if content is blank. Always pass a non-empty content value.

[!TIP] If multiple tooltips appear on the same page, you do not need to worry about duplicate &lt;style> or &lt;script> tags โ€” the partial injects them only once via ViewData.

[!NOTE] The outer wrapper uses position: relative and z-index: 10 at rest, rising to 99999 on hover. Make sure parent containers do not have overflow: hidden or a lower z-index that clips the bubble.