PartsIntersect
In the week before their departure to Arrakis, when all
the final scurrying about had reached a nearly unbearable frenzy, an old crone came to visit the mother of the boy, Paul.
It was a warm night at Castle Caladan, and the ancient pile of stone that had served the Atreides family as home for twenty-six generations bore that cooled-sweat feeling it acquired before a change in the weather.
The old woman was let in by the side door down
the vaulted passage by Paul’s room and she was allowed a moment to peer in at him where he lay in his bed.
By the half-light of a suspensor lamp, dimmed and hanging near the floor, the awakened boy could see a bulky female shape at his door, standing one step ahead of his mother. The old woman was a witch shadow hair like matted spiderwebs, hooded ’round darkness of features, eyes like glittering jewels.
“Is he not small for his age, Jessica?” the old woman asked. Her voice wheezed and twanged
like an untuned baliset.
Paul’s mother answered in her soft contralto: “The Atreides are known to start late getting their growth, Your Reverence.”
“So I’ve heard, so I’ve heard,” wheezed the old woman. “Yet he’s already fifteen.”
“Yes, Your Reverence.”

$lib/util/tooltip.ts

export function tooltip(element: HTMLElement, options: any) {
	// tooltip svelte action

	let content = options.content;

	const { offsetTop, offsetLeft, offsetWidth } = element;

	// create tooltip Element
	const tooltip = document.createElement('div');
	tooltip.className = 'tooltip';
	tooltip.innerHTML = options.content;
	tooltip.style.position = 'absolute';
	tooltip.style.top = `${offsetTop}px`;
	tooltip.style.left = `${offsetLeft + offsetWidth / 2}px`;
	tooltip.style.zIndex = '9999';
	tooltip.style.pointerEvents = 'none';
	tooltip.style.opacity = '0';
	tooltip.style.transition = 'all 0.3s ease-in-out';
	tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
	tooltip.style.color = '#fff';
	tooltip.style.borderRadius = '0.25rem';
	tooltip.style.fontSize = '0.75rem';
	tooltip.style.fontWeight = '400';
	tooltip.style.padding = '0.5rem 1rem';
	tooltip.style.lineHeight = '1.25rem';
	tooltip.style.width = 'fit-content';
	tooltip.style.maxWidth = '200px';
	tooltip.style.textAlign = 'center';
	tooltip.style.boxShadow = '0 0.5rem 1rem rgba(0, 0, 0, 0.15)';
	tooltip.style.transform = 'translate(-50%, calc(-100% - 0.5rem))';
	tooltip.style.transformOrigin = 'center';
	tooltip.style.willChange = 'transform, opacity';
	tooltip.style.userSelect = 'none';

	// add tooltip to DOM
	// element.appendChild(tooltip);

	// append to document
	document.body.appendChild(tooltip);

	element.addEventListener('mouseenter', () => {
		if (content.length <= 0) return;
		tooltip.style.opacity = '1';
		tooltip.style.transform = 'translate(-50%, calc(-100% - 0.3rem))';
	});

	element.addEventListener('mouseleave', () => {
		tooltip.style.opacity = '0';
		tooltip.style.transform = 'translate(-50%, calc(-100% - 0.5rem))';
	});

	// add resize listener
	window.addEventListener('resize', () => {
		const { offsetTop, offsetLeft, offsetWidth } = element;
		tooltip.style.left = `${offsetLeft + offsetWidth / 2}px`;
		tooltip.style.top = `${offsetTop}px`;
	});

	return {
		update(options: any) {
			content = options.content;
			tooltip.innerHTML = options.content;
		},
		destroy() {
			// cleanup
			tooltip.remove();
		}
	};
}