14 / 21
All 21 in 21 Tailwind CSS Tooltips
- 01Tailwind Schematic Hotspot Tooltip with Pulsing CAD Callouts
- 02Tailwind Keyboard Shortcut Tooltip with Animated 3D Keycaps
- 03Tailwind IDE Function Signature Tooltip with Typed Docs Popover
- 04Tailwind Editorial Footnote Tooltip with Torn-Paper Sidenote
- 05Tailwind Profile Hover Card Tooltip with Staggered Reveal
- 06Tailwind RPG Inventory Tooltip with Rarity-Tinted Item Cards
- 07Tailwind Glossary Word Tooltip with Dictionary Definition Card
- 08Tailwind Service Status Tooltip with 30-Day Uptime History
- 09Tailwind Team Avatar Hover Card with Presence and Quick Actions
- 10Tailwind Keyboard Shortcut Reference Tooltip with When-To-Use Card
- 11Tailwind Data Table Cell Tooltip with Segment Breakdown Bars
- 12Tailwind Content Tag Tooltip with Topic Detail and Popularity Bar
- 13Tailwind Airport Departures Board Tooltip with Flight Detail Card
- 14Tailwind Recipe Ingredient Tooltip with Nutrition and Substitutions
- 15Tailwind Constellation Star Chart Tooltip with Astronomical Data
- 16Tailwind Periodic Table Element Tooltip with Category Colour Coding
- 17Tailwind Photo Grid EXIF Tooltip with Camera Settings Card
- 18Tailwind Org Chart Node Tooltip with Reporting-Line Detail Card
- 19Tailwind Company Timeline Tooltip with Alternating Event Cards
- 20Tailwind Icon Toolbar Tooltip with Pure-CSS Arrow Pill
- 21Tailwind Form Field Help Tooltip with Side-Anchored Hint Pill
Tailwind Recipe Ingredient Tooltip with Nutrition and Substitutions
A cookbook page where each ingredient carries a dotted underline and opens a side card with nutrition, substitutions and a sourcing note. Warm paper palette, Georgia for the recipe, Inter inside the card — the port's value is that the two typographic registers are now visible per element instead of split across sixteen rules.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttp-14 w-full min-h-svh bg-ttp14-stage py-10 pr-[320px] pl-7 font-ttp14-serif max-xl:pr-7">
<article class="mx-auto max-w-[480px] rounded-[2px] border border-ttp14-edge bg-white px-9 py-8 shadow-[0_2px_22px_rgba(100,80,40,0.08),0_0_0_4px_rgba(180,160,120,0.06)]">
<h2 class="m-0 mb-1 text-[26px] italic leading-tight text-ttp14-ink">Roasted Lemon Herb Chicken</h2>
<div class="mb-5 text-xs uppercase tracking-[0.08em] text-ttp14-meta">French Country · Serves 4</div>
<div class="mb-5.5 flex flex-wrap gap-5 border-b border-ttp14-edge pb-4">
<div><div class="ttp14-mlabel">Prep</div><div class="ttp14-mval">20 min</div></div>
<div><div class="ttp14-mlabel">Cook</div><div class="ttp14-mval">1 hr 15 min</div></div>
<div><div class="ttp14-mlabel">Total</div><div class="ttp14-mval">1 hr 35 min</div></div>
<div><div class="ttp14-mlabel">Difficulty</div><div class="ttp14-mval">Medium</div></div>
</div>
<h3 class="mb-3 text-xs font-normal uppercase tracking-[0.12em] text-ttp14-head">Ingredients</h3>
<ul class="m-0 list-none p-0">
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">1 whole (3–4 lb)</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i1" class="ttp14-name">free-range chicken</button>
<span id="ttp14-i1" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Free-Range Chicken</span><span class="ttp14-ctype">Poultry · whole bird</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 100g (roasted, no skin)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">165 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[55%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Protein</span><span class="ttp14-nval">31g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.1mg (14% DV)</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Cornish game hens (halve cook time)</span><span class="ttp14-sub">Bone-in chicken pieces</span></span>
<span class="ttp14-note">Look for air-chilled, free-range birds — less water retention gives crispier skin.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 whole heads</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i2" class="ttp14-name">garlic</button>
<span id="ttp14-i2" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Garlic</span><span class="ttp14-ctype">Allium · bulb</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 3 cloves (9g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">13 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[10%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Allicin</span><span class="ttp14-nval">high (antimicrobial)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vit C</span><span class="ttp14-nval">9% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Roasted garlic paste (2 tbsp)</span><span class="ttp14-sub">Shallots (milder flavour)</span></span>
<span class="ttp14-note">Do not peel — roasted whole heads caramelise beautifully.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 medium</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i3" class="ttp14-name">lemons, zested and halved</button>
<span id="ttp14-i3" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Lemon</span><span class="ttp14-ctype">Citrus fruit · fresh</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 1 medium lemon (84g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">22 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[15%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin C</span><span class="ttp14-nval">51mg (56% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Citric acid</span><span class="ttp14-nval">tenderises meat</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Meyer lemons (sweeter)</span><span class="ttp14-sub">1½ limes + ½ orange</span></span>
<span class="ttp14-note">Microplane the zest before cutting — releases essential oils lost once sliced.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">¼ cup, fresh</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i4" class="ttp14-name">thyme and rosemary</button>
<span id="ttp14-i4" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Fresh Herbs</span><span class="ttp14-ctype">Lamiaceae · aromatic</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp fresh thyme (2.4g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Thymol</span><span class="ttp14-nval">antimicrobial terpene</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[30%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.7mg (10% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Rosmarinic</span><span class="ttp14-nval">antioxidant</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Dried herbs (use ⅓ the quantity)</span><span class="ttp14-sub">Herbes de Provence (1 tbsp)</span></span>
<span class="ttp14-note">Slide whole sprigs under the skin — the fat bastes the herbs during roasting.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">4 tbsp, softened</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i5" class="ttp14-name">unsalted butter</button>
<span id="ttp14-i5" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Unsalted Butter</span><span class="ttp14-ctype">Dairy · fat</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp (14g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">102 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[78%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Saturated</span><span class="ttp14-nval">7.3g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin A</span><span class="ttp14-nval">7% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Ghee (higher smoke point)</span><span class="ttp14-sub">Olive oil (dairy-free)</span></span>
<span class="ttp14-note">Unsalted lets you control seasoning. European-style (84%+ fat) gives richer results.</span>
</span>
</span>
</li>
</ul>
</article>
</section><section class="ttp-14 w-full min-h-svh bg-ttp14-stage py-10 pr-[320px] pl-7 font-ttp14-serif max-xl:pr-7">
<article class="mx-auto max-w-[480px] rounded-[2px] border border-ttp14-edge bg-white px-9 py-8 shadow-[0_2px_22px_rgba(100,80,40,0.08),0_0_0_4px_rgba(180,160,120,0.06)]">
<h2 class="m-0 mb-1 text-[26px] italic leading-tight text-ttp14-ink">Roasted Lemon Herb Chicken</h2>
<div class="mb-5 text-xs uppercase tracking-[0.08em] text-ttp14-meta">French Country · Serves 4</div>
<div class="mb-5.5 flex flex-wrap gap-5 border-b border-ttp14-edge pb-4">
<div><div class="ttp14-mlabel">Prep</div><div class="ttp14-mval">20 min</div></div>
<div><div class="ttp14-mlabel">Cook</div><div class="ttp14-mval">1 hr 15 min</div></div>
<div><div class="ttp14-mlabel">Total</div><div class="ttp14-mval">1 hr 35 min</div></div>
<div><div class="ttp14-mlabel">Difficulty</div><div class="ttp14-mval">Medium</div></div>
</div>
<h3 class="mb-3 text-xs font-normal uppercase tracking-[0.12em] text-ttp14-head">Ingredients</h3>
<ul class="m-0 list-none p-0">
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">1 whole (3–4 lb)</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i1" class="ttp14-name">free-range chicken</button>
<span id="ttp14-i1" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Free-Range Chicken</span><span class="ttp14-ctype">Poultry · whole bird</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 100g (roasted, no skin)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">165 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[55%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Protein</span><span class="ttp14-nval">31g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.1mg (14% DV)</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Cornish game hens (halve cook time)</span><span class="ttp14-sub">Bone-in chicken pieces</span></span>
<span class="ttp14-note">Look for air-chilled, free-range birds — less water retention gives crispier skin.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 whole heads</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i2" class="ttp14-name">garlic</button>
<span id="ttp14-i2" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Garlic</span><span class="ttp14-ctype">Allium · bulb</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 3 cloves (9g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">13 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[10%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Allicin</span><span class="ttp14-nval">high (antimicrobial)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vit C</span><span class="ttp14-nval">9% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Roasted garlic paste (2 tbsp)</span><span class="ttp14-sub">Shallots (milder flavour)</span></span>
<span class="ttp14-note">Do not peel — roasted whole heads caramelise beautifully.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 medium</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i3" class="ttp14-name">lemons, zested and halved</button>
<span id="ttp14-i3" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Lemon</span><span class="ttp14-ctype">Citrus fruit · fresh</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 1 medium lemon (84g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">22 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[15%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin C</span><span class="ttp14-nval">51mg (56% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Citric acid</span><span class="ttp14-nval">tenderises meat</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Meyer lemons (sweeter)</span><span class="ttp14-sub">1½ limes + ½ orange</span></span>
<span class="ttp14-note">Microplane the zest before cutting — releases essential oils lost once sliced.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">¼ cup, fresh</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i4" class="ttp14-name">thyme and rosemary</button>
<span id="ttp14-i4" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Fresh Herbs</span><span class="ttp14-ctype">Lamiaceae · aromatic</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp fresh thyme (2.4g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Thymol</span><span class="ttp14-nval">antimicrobial terpene</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[30%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.7mg (10% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Rosmarinic</span><span class="ttp14-nval">antioxidant</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Dried herbs (use ⅓ the quantity)</span><span class="ttp14-sub">Herbes de Provence (1 tbsp)</span></span>
<span class="ttp14-note">Slide whole sprigs under the skin — the fat bastes the herbs during roasting.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">4 tbsp, softened</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i5" class="ttp14-name">unsalted butter</button>
<span id="ttp14-i5" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Unsalted Butter</span><span class="ttp14-ctype">Dairy · fat</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp (14g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">102 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[78%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Saturated</span><span class="ttp14-nval">7.3g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin A</span><span class="ttp14-nval">7% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Ghee (higher smoke point)</span><span class="ttp14-sub">Olive oil (dairy-free)</span></span>
<span class="ttp14-note">Unsalted lets you control seasoning. European-style (84%+ fat) gives richer results.</span>
</span>
</span>
</li>
</ul>
</article>
</section>@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Two font registers inside one component is where utilities read best:
font-ttp14-serif italiclands on exactly the four elements that are editorial, and the rest inherits Inter from the card. - Five ingredients share one card anatomy, so the shell and its six sections are
@applyclasses; only the nutrition numbers and the bar width live in the markup. - UI change from the CSS original: ingredient names are
<button>elements witharia-describedbyand focus twins; every sandy grey (#a09070,#b0a080,#9a8a68,#7a6a50) was darkened to 0.47–0.54-lightness tokens, the 8.5px labels went to 9px and the 9.5px italic note to 10.5px, and the card stacks below its line under 1280px.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Recipe Ingredient Tooltip with Nutrition and Substitutions
Source: https://codefronts.com/snippets/tailwind-css-tooltips/tailwind-recipe-ingredient-tooltip-nutrition-substitutions/
A cookbook page where each ingredient carries a dotted underline and opens a side card with nutrition, substitutions and a sourcing note. Warm paper palette, Georgia for the recipe, Inter inside the card — the port's value is that the two typographic registers are now visible per element instead of split across sixteen rules.
## HTML
```html
<section class="ttp-14 w-full min-h-svh bg-ttp14-stage py-10 pr-[320px] pl-7 font-ttp14-serif max-xl:pr-7">
<article class="mx-auto max-w-[480px] rounded-[2px] border border-ttp14-edge bg-white px-9 py-8 shadow-[0_2px_22px_rgba(100,80,40,0.08),0_0_0_4px_rgba(180,160,120,0.06)]">
<h2 class="m-0 mb-1 text-[26px] italic leading-tight text-ttp14-ink">Roasted Lemon Herb Chicken</h2>
<div class="mb-5 text-xs uppercase tracking-[0.08em] text-ttp14-meta">French Country · Serves 4</div>
<div class="mb-5.5 flex flex-wrap gap-5 border-b border-ttp14-edge pb-4">
<div><div class="ttp14-mlabel">Prep</div><div class="ttp14-mval">20 min</div></div>
<div><div class="ttp14-mlabel">Cook</div><div class="ttp14-mval">1 hr 15 min</div></div>
<div><div class="ttp14-mlabel">Total</div><div class="ttp14-mval">1 hr 35 min</div></div>
<div><div class="ttp14-mlabel">Difficulty</div><div class="ttp14-mval">Medium</div></div>
</div>
<h3 class="mb-3 text-xs font-normal uppercase tracking-[0.12em] text-ttp14-head">Ingredients</h3>
<ul class="m-0 list-none p-0">
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">1 whole (3–4 lb)</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i1" class="ttp14-name">free-range chicken</button>
<span id="ttp14-i1" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Free-Range Chicken</span><span class="ttp14-ctype">Poultry · whole bird</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 100g (roasted, no skin)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">165 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[55%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Protein</span><span class="ttp14-nval">31g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.1mg (14% DV)</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Cornish game hens (halve cook time)</span><span class="ttp14-sub">Bone-in chicken pieces</span></span>
<span class="ttp14-note">Look for air-chilled, free-range birds — less water retention gives crispier skin.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 whole heads</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i2" class="ttp14-name">garlic</button>
<span id="ttp14-i2" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Garlic</span><span class="ttp14-ctype">Allium · bulb</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 3 cloves (9g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">13 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[10%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Allicin</span><span class="ttp14-nval">high (antimicrobial)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vit C</span><span class="ttp14-nval">9% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Roasted garlic paste (2 tbsp)</span><span class="ttp14-sub">Shallots (milder flavour)</span></span>
<span class="ttp14-note">Do not peel — roasted whole heads caramelise beautifully.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 medium</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i3" class="ttp14-name">lemons, zested and halved</button>
<span id="ttp14-i3" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Lemon</span><span class="ttp14-ctype">Citrus fruit · fresh</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 1 medium lemon (84g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">22 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[15%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin C</span><span class="ttp14-nval">51mg (56% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Citric acid</span><span class="ttp14-nval">tenderises meat</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Meyer lemons (sweeter)</span><span class="ttp14-sub">1½ limes + ½ orange</span></span>
<span class="ttp14-note">Microplane the zest before cutting — releases essential oils lost once sliced.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">¼ cup, fresh</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i4" class="ttp14-name">thyme and rosemary</button>
<span id="ttp14-i4" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Fresh Herbs</span><span class="ttp14-ctype">Lamiaceae · aromatic</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp fresh thyme (2.4g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Thymol</span><span class="ttp14-nval">antimicrobial terpene</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[30%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.7mg (10% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Rosmarinic</span><span class="ttp14-nval">antioxidant</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Dried herbs (use ⅓ the quantity)</span><span class="ttp14-sub">Herbes de Provence (1 tbsp)</span></span>
<span class="ttp14-note">Slide whole sprigs under the skin — the fat bastes the herbs during roasting.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">4 tbsp, softened</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i5" class="ttp14-name">unsalted butter</button>
<span id="ttp14-i5" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Unsalted Butter</span><span class="ttp14-ctype">Dairy · fat</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp (14g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">102 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[78%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Saturated</span><span class="ttp14-nval">7.3g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin A</span><span class="ttp14-nval">7% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Ghee (higher smoke point)</span><span class="ttp14-sub">Olive oil (dairy-free)</span></span>
<span class="ttp14-note">Unsalted lets you control seasoning. European-style (84%+ fat) gives richer results.</span>
</span>
</span>
</li>
</ul>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Recipe Ingredient Tooltip with Nutrition and Substitutions
Source: https://codefronts.com/snippets/tailwind-css-tooltips/tailwind-recipe-ingredient-tooltip-nutrition-substitutions/
A cookbook page where each ingredient carries a dotted underline and opens a side card with nutrition, substitutions and a sourcing note. Warm paper palette, Georgia for the recipe, Inter inside the card — the port's value is that the two typographic registers are now visible per element instead of split across sixteen rules.
## HTML
```html
<section class="ttp-14 w-full min-h-svh bg-ttp14-stage py-10 pr-[320px] pl-7 font-ttp14-serif max-xl:pr-7">
<article class="mx-auto max-w-[480px] rounded-[2px] border border-ttp14-edge bg-white px-9 py-8 shadow-[0_2px_22px_rgba(100,80,40,0.08),0_0_0_4px_rgba(180,160,120,0.06)]">
<h2 class="m-0 mb-1 text-[26px] italic leading-tight text-ttp14-ink">Roasted Lemon Herb Chicken</h2>
<div class="mb-5 text-xs uppercase tracking-[0.08em] text-ttp14-meta">French Country · Serves 4</div>
<div class="mb-5.5 flex flex-wrap gap-5 border-b border-ttp14-edge pb-4">
<div><div class="ttp14-mlabel">Prep</div><div class="ttp14-mval">20 min</div></div>
<div><div class="ttp14-mlabel">Cook</div><div class="ttp14-mval">1 hr 15 min</div></div>
<div><div class="ttp14-mlabel">Total</div><div class="ttp14-mval">1 hr 35 min</div></div>
<div><div class="ttp14-mlabel">Difficulty</div><div class="ttp14-mval">Medium</div></div>
</div>
<h3 class="mb-3 text-xs font-normal uppercase tracking-[0.12em] text-ttp14-head">Ingredients</h3>
<ul class="m-0 list-none p-0">
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">1 whole (3–4 lb)</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i1" class="ttp14-name">free-range chicken</button>
<span id="ttp14-i1" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Free-Range Chicken</span><span class="ttp14-ctype">Poultry · whole bird</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 100g (roasted, no skin)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">165 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[55%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Protein</span><span class="ttp14-nval">31g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.1mg (14% DV)</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Cornish game hens (halve cook time)</span><span class="ttp14-sub">Bone-in chicken pieces</span></span>
<span class="ttp14-note">Look for air-chilled, free-range birds — less water retention gives crispier skin.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 whole heads</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i2" class="ttp14-name">garlic</button>
<span id="ttp14-i2" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Garlic</span><span class="ttp14-ctype">Allium · bulb</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 3 cloves (9g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">13 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[10%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Allicin</span><span class="ttp14-nval">high (antimicrobial)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vit C</span><span class="ttp14-nval">9% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Roasted garlic paste (2 tbsp)</span><span class="ttp14-sub">Shallots (milder flavour)</span></span>
<span class="ttp14-note">Do not peel — roasted whole heads caramelise beautifully.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">2 medium</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i3" class="ttp14-name">lemons, zested and halved</button>
<span id="ttp14-i3" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Lemon</span><span class="ttp14-ctype">Citrus fruit · fresh</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per 1 medium lemon (84g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">22 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[15%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin C</span><span class="ttp14-nval">51mg (56% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Citric acid</span><span class="ttp14-nval">tenderises meat</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Meyer lemons (sweeter)</span><span class="ttp14-sub">1½ limes + ½ orange</span></span>
<span class="ttp14-note">Microplane the zest before cutting — releases essential oils lost once sliced.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 border-b border-dashed border-ttp14-edge text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">¼ cup, fresh</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i4" class="ttp14-name">thyme and rosemary</button>
<span id="ttp14-i4" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Fresh Herbs</span><span class="ttp14-ctype">Lamiaceae · aromatic</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp fresh thyme (2.4g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Thymol</span><span class="ttp14-nval">antimicrobial terpene</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[30%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Iron</span><span class="ttp14-nval">1.7mg (10% DV)</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Rosmarinic</span><span class="ttp14-nval">antioxidant</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Dried herbs (use ⅓ the quantity)</span><span class="ttp14-sub">Herbes de Provence (1 tbsp)</span></span>
<span class="ttp14-note">Slide whole sprigs under the skin — the fat bastes the herbs during roasting.</span>
</span>
</span>
</li>
<li class="flex items-baseline gap-1.5 text-[15px] leading-[1.9] text-ttp14-ink">
<span class="ttp14-qty">4 tbsp, softened</span>
<span class="group relative inline-block">
<button type="button" aria-describedby="ttp14-i5" class="ttp14-name">unsalted butter</button>
<span id="ttp14-i5" role="tooltip" class="ttp14-card">
<span class="ttp14-chead"><span class="ttp14-cname">Unsalted Butter</span><span class="ttp14-ctype">Dairy · fat</span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Per tbsp (14g)</span><span class="ttp14-nrow"><span class="ttp14-nname">Calories</span><span class="ttp14-nval">102 kcal</span></span><span class="ttp14-bar"><span class="ttp14-fill w-[78%]"></span></span><span class="ttp14-nrow"><span class="ttp14-nname">Saturated</span><span class="ttp14-nval">7.3g</span></span><span class="ttp14-nrow"><span class="ttp14-nname">Vitamin A</span><span class="ttp14-nval">7% DV</span></span></span>
<span class="ttp14-sec"><span class="ttp14-slabel">Substitutions</span><span class="ttp14-sub">Ghee (higher smoke point)</span><span class="ttp14-sub">Olive oil (dairy-free)</span></span>
<span class="ttp14-note">Unsalted lets you control seasoning. European-style (84%+ fat) gives richer results.</span>
</span>
</span>
</li>
</ul>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttp14-stage: #fdf8f0;
--color-ttp14-stage: oklch(0.98 0.015 85);
--color-ttp14-well: #f8f2e6;
--color-ttp14-well: oklch(0.96 0.018 85);
--color-ttp14-edge: #e0d4bd;
--color-ttp14-edge: oklch(0.87 0.03 85);
--color-ttp14-ink: #3a2c18;
--color-ttp14-ink: oklch(0.31 0.04 70);
--color-ttp14-body: #6a5a3e;
--color-ttp14-body: oklch(0.47 0.05 78);
--color-ttp14-meta: #7d6c4c;
--color-ttp14-meta: oklch(0.54 0.05 80);
--color-ttp14-head: #705a3a;
--color-ttp14-head: oklch(0.5 0.05 75);
--color-ttp14-accent: #8a5020;
--color-ttp14-accent: oklch(0.47 0.1 55);
--color-ttp14-bar1: #c8a060;
--color-ttp14-bar2: #e8c080;
--font-ttp14-serif: Georgia, 'Times New Roman', serif;
--font-ttp14-sans: 'Inter', system-ui, sans-serif;
}
.ttp-14 {
width: 100%;
min-height: 100svh;
}
/* Five ingredients, one card anatomy: header, nutrition, subs, note. */
.ttp-14 .ttp14-mlabel {
@apply text-[9px] uppercase tracking-[0.12em] text-ttp14-meta;
}
.ttp-14 .ttp14-mval {
@apply mt-px text-[13px] italic text-ttp14-body;
}
.ttp-14 .ttp14-qty {
@apply min-w-[110px] shrink-0 text-[13px] italic text-ttp14-meta;
}
.ttp-14 .ttp14-name {
@apply cursor-help border-0 border-b border-dotted border-ttp14-accent/50 bg-transparent p-0 font-ttp14-serif text-[15px] text-ttp14-ink transition-colors duration-150 hover:border-ttp14-accent hover:text-ttp14-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp14-accent;
}
.ttp-14 .ttp14-card {
@apply pointer-events-none invisible absolute top-1/2 left-[calc(100%+14px)] z-50 block w-60 max-w-[calc(100vw-48px)] -translate-y-1/2 translate-x-1.5 overflow-hidden rounded border border-ttp14-edge bg-white text-left font-ttp14-sans opacity-0 shadow-[4px_6px_22px_rgba(80,60,20,0.15)] transition-[opacity,translate,visibility] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:visible group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:visible group-focus-within:translate-x-0 group-focus-within:opacity-100;
}
.ttp-14 .ttp14-chead {
@apply block border-b border-ttp14-edge bg-ttp14-well px-3.25 py-2.75;
}
.ttp-14 .ttp14-cname {
@apply mb-0.5 block font-ttp14-serif text-sm italic text-ttp14-ink;
}
.ttp-14 .ttp14-ctype {
@apply block text-[10px] uppercase tracking-[0.08em] text-ttp14-meta;
}
.ttp-14 .ttp14-sec {
@apply block border-b border-ttp14-edge px-3.25 py-2.5;
}
.ttp-14 .ttp14-slabel {
@apply mb-1.5 block text-[9px] uppercase tracking-[0.1em] text-ttp14-meta;
}
.ttp-14 .ttp14-nrow {
@apply mb-0.75 flex justify-between text-[10.5px];
}
.ttp-14 .ttp14-nname {
@apply text-ttp14-body;
}
.ttp-14 .ttp14-nval {
@apply font-ttp14-serif italic text-ttp14-ink;
}
.ttp-14 .ttp14-bar {
@apply my-0.5 mb-1.5 block h-0.5 overflow-hidden rounded-[1px] bg-ttp14-edge;
}
.ttp-14 .ttp14-fill {
@apply block h-full rounded-[1px] bg-linear-to-r from-ttp14-bar1 to-ttp14-bar2;
}
.ttp-14 .ttp14-sub {
@apply relative mb-1 block pl-2.25 text-[10.5px] text-ttp14-body before:absolute before:left-0 before:text-[9px] before:text-ttp14-accent before:content-['→'];
}
.ttp-14 .ttp14-note {
@apply block px-3.25 py-2.5 font-ttp14-serif text-[10.5px] italic leading-[1.5] text-ttp14-body;
}
@media (max-width: 1279px) {
.ttp-14 .ttp14-card {
@apply static hidden mt-2 w-full translate-x-0 translate-y-0 group-hover:block group-focus-within:block;
}
}
```How this works
Two type registers, one component. The recipe is Georgia; the card is Inter with Georgia italic for values. Utilities put font-ttp14-serif italic exactly on the elements that need it, which is how the card reads as a reference insert rather than more recipe.
The card opens to the side so it never covers the ingredient list you are reading — the one layout decision that makes a cooking tooltip usable while cooking.
Dotted underline, not colour. The affordance is a border that darkens on hover and focus, so the ingredient names stay black-ink readable in the list.
Make it yours
- Paper and ink: six warm tokens. Cooling them to greys gives a clinical nutrition-database look.
- Bar gradient:
--color-ttp14-bar1/bar2— the only chart colour in the demo. - Card side: swap to
right-[calc(100%+14px)]for right-to-left layouts.
Gotchas — read before shipping
- A side card needs a gutter; under 1280px this port stacks the card below its line instead of letting it overflow.
- Georgia italic at 9.5px was unreadable — notes are 10.5px here, and all the sandy greys were darkened.
- Ingredient triggers must be focusable: hovering a word is not an interaction a keyboard can perform.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
flex and transitions only; the floor quoted is Tailwind v4's own generated sheet.