diff --git a/src/app/shared/components/tooltip/tooltip.component.html b/src/app/shared/components/tooltip/tooltip.component.html
new file mode 100644
index 0000000..35788c8
--- /dev/null
+++ b/src/app/shared/components/tooltip/tooltip.component.html
@@ -0,0 +1,11 @@
+
diff --git a/src/app/shared/components/tooltip/tooltip.component.scss b/src/app/shared/components/tooltip/tooltip.component.scss
new file mode 100644
index 0000000..c195b68
--- /dev/null
+++ b/src/app/shared/components/tooltip/tooltip.component.scss
@@ -0,0 +1,62 @@
+:host {
+ display: inline-flex;
+ align-items: center;
+ margin-left: 0.25rem;
+ vertical-align: middle;
+}
+
+.tooltip-trigger {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ color: var(--tenant-primary, var(--color-primary, #0d6efd));
+ font-size: 1em;
+ line-height: inherit;
+
+ &:focus-visible {
+ border-radius: 50%;
+ outline: 2px solid currentColor;
+ outline-offset: 2px;
+ }
+
+ &:hover .tooltip-message,
+ &:focus-visible .tooltip-message {
+ visibility: visible;
+ opacity: 1;
+ transform: translate(-50%, -0.25rem);
+ }
+}
+
+.tooltip-message {
+ position: absolute;
+ z-index: 1100;
+ bottom: calc(100% + 0.625rem);
+ left: 50%;
+ width: max-content;
+ max-width: min(16rem, 75vw);
+ padding: 0.5rem 0.75rem;
+ border-radius: 0.375rem;
+ border: 1px solid var(--tenant-primary, var(--color-primary, #0d6efd));
+ background: #fff;
+ color: #212529;
+ font-family: inherit;
+ font-size: 0.75rem;
+ font-weight: 400;
+ line-height: 1.35;
+ text-align: center;
+ text-transform: none;
+ letter-spacing: normal;
+ white-space: normal;
+ visibility: hidden;
+ opacity: 0;
+ pointer-events: none;
+ transform: translate(-50%, 0);
+ transition:
+ opacity 0.15s ease,
+ transform 0.15s ease,
+ visibility 0.15s ease;
+}
diff --git a/src/app/shared/components/tooltip/tooltip.component.spec.ts b/src/app/shared/components/tooltip/tooltip.component.spec.ts
new file mode 100644
index 0000000..1702ac7
--- /dev/null
+++ b/src/app/shared/components/tooltip/tooltip.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, getTestBed } from '@angular/core/testing';
+import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
+import { afterEach, beforeAll, describe, expect, it } from 'vitest';
+
+import { TooltipComponent } from './tooltip.component';
+
+describe('TooltipComponent', () => {
+ beforeAll(() => {
+ try {
+ getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
+ } catch {
+ // Test environment may already be initialized by another setup entrypoint.
+ }
+ });
+
+ afterEach(() => TestBed.resetTestingModule());
+
+ it('renders the reusable danger information tooltip', async () => {
+ await TestBed.configureTestingModule({ imports: [TooltipComponent] }).compileComponents();
+ const fixture = TestBed.createComponent(TooltipComponent);
+ fixture.componentRef.setInput('message', 'Este producto no tiene stock disponible.');
+ fixture.detectChanges();
+
+ const element = fixture.nativeElement as HTMLElement;
+ const trigger = element.querySelector('.tooltip-trigger');
+ const message = element.querySelector('.tooltip-message');
+
+ expect(trigger?.querySelector('i.fa-solid.fa-circle-info')).not.toBeNull();
+ expect(message?.textContent?.trim()).toBe('Este producto no tiene stock disponible.');
+ expect(trigger?.getAttribute('aria-describedby')).toBe(message?.id);
+ });
+});
diff --git a/src/app/shared/components/tooltip/tooltip.component.ts b/src/app/shared/components/tooltip/tooltip.component.ts
new file mode 100644
index 0000000..9f70c23
--- /dev/null
+++ b/src/app/shared/components/tooltip/tooltip.component.ts
@@ -0,0 +1,14 @@
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+
+let nextTooltipId = 0;
+
+@Component({
+ selector: 'app-tooltip',
+ templateUrl: './tooltip.component.html',
+ styleUrl: './tooltip.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class TooltipComponent {
+ readonly message = input.required();
+ protected readonly tooltipId = `app-tooltip-${nextTooltipId++}`;
+}