-
Inline Text Usage
-
- You can also use tooltips on standard inline text. For example, hover over this
- magic word
- to learn more about it without using any Javascript.
+
+
+
+
+
Section 02
+
Icon Button Toolbar
+
+ A common real-world pattern — icon-only buttons with tooltips explaining
+ each action. No visible label needed; the tooltip carries the meaning.
+ Keyboard accessible via focus-within .
+
+
+
+
+ 📄
+
+
+
+ 📁
+
+
+
+ 💾
+
+
+
+ 📋
+
+
+
+ ✂️
+
+
+
+ 🔍
+
+
+
+ ⚙️
+
+
+
+ 🗑️
+
+
+
-
+
+
+
+
+
Section 03
+
Semantic Color Variants
+
+ Tooltip background color is driven by a single
+ --tooltip-bg CSS variable.
+ Semantic variants (success, danger, warning, info) reinforce
+ the intent of the action — no extra markup needed.
+
+
+
+
+
+ Default
+
+
+
+ ✓ Success
+
+
+
+ ✕ Danger
+
+
+
+ ⚠ Warning
+
+
+
+ ℹ Info
+
+
+
+
+
+
+
+
+
+
Section 04
+
Inline Prose Usage
+
+ Tooltips work inline inside paragraphs — useful for glossary terms,
+ abbreviations, and contextual hints without breaking reading flow.
+
+
+
+
+ EaseMotion CSS uses
+
+ CSS custom properties
+
+ as its token system. All animations respect
+
+ prefers-reduced-motion
+
+ and are triggered via
+
+ :focus-within
+
+ for full keyboard accessibility — no JavaScript required.
+
+
+
+
+
+
+
+
+
Section 05
+
Keyboard Accessible
+
+ Tab through the buttons below. The tooltip appears on
+ :focus-within — no mouse required.
+ This makes the component WCAG 2.1 AA compliant out of the box.
+
+
+
+
+
+ Tab → me
+
+
+
+ Then → me
+
+
+
+ Then → me
+
+
+
+
-
+
\ No newline at end of file
diff --git a/submissions/examples/ease-tooltip/style.css b/submissions/examples/ease-tooltip/style.css
index 697f805..6712d59 100644
--- a/submissions/examples/ease-tooltip/style.css
+++ b/submissions/examples/ease-tooltip/style.css
@@ -1,130 +1,276 @@
/* ============================================================
- EaseMotion CSS — ease-tooltip component (Proposal)
- CSS-only tooltips using data-tooltip attributes.
+ EaseMotion CSS — Contributor Submission
+ Feature: CSS-only Animated Tooltip Component
+ Folder: submissions/examples/ease-tooltip/
+ Author: AjayBandiwaddar
+ ============================================================
+
+ NAMING NOTE:
+ Class names here are contributor-defined.
+ The maintainer will rename to ease-* convention before merging.
============================================================ */
-/* ── Base Tooltip Container ────────────────────────────────── */
-.ease-tooltip {
+
+/* ── Design Tokens (mirrors core/variables.css) ────────────
+ All values reference --ease-* custom properties.
+ Hard-coded fallbacks are provided only for standalone
+ demo use — the maintainer will remove them on integration.
+ ──────────────────────────────────────────────────────────── */
+
+:root {
+ --tooltip-bg: var(--ease-color-neutral-900, #0f172a);
+ --tooltip-color: var(--ease-color-neutral-50, #f8fafc);
+ --tooltip-font-size: var(--ease-text-xs, 0.75rem);
+ --tooltip-radius: var(--ease-radius-sm, 0.25rem);
+ --tooltip-padding-y: var(--ease-space-1, 0.25rem);
+ --tooltip-padding-x: var(--ease-space-3, 0.75rem);
+ --tooltip-speed: var(--ease-speed-fast, 150ms);
+ --tooltip-ease: var(--ease-ease-out, cubic-bezier(0, 0, 0.2, 1));
+ --tooltip-offset: 8px; /* gap between trigger and bubble */
+ --tooltip-arrow-size: 5px; /* half-width of the arrow tip */
+ --tooltip-max-width: 220px;
+}
+
+
+/* ── Wrapper ───────────────────────────────────────────────
+ .tooltip wraps any trigger element.
+ display: inline-flex keeps it tight around the child.
+ ──────────────────────────────────────────────────────────── */
+
+.tooltip {
position: relative;
- display: inline-block;
- cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
}
-/* ── Base Tooltip Bubble (::after) & Arrow (::before) ──────── */
-.ease-tooltip::after,
-.ease-tooltip::before {
+
+/* ── Bubble (::before) ─────────────────────────────────────
+ Content is pulled from data-tip attribute.
+ ──────────────────────────────────────────────────────────── */
+
+.tooltip::before {
+ content: attr(data-tip);
position: absolute;
- opacity: 0;
- visibility: hidden;
- pointer-events: none;
- z-index: var(--ease-z-overlay);
- transition: opacity var(--ease-speed-fast) var(--ease-ease),
- transform var(--ease-speed-fast) var(--ease-ease),
- visibility var(--ease-speed-fast) var(--ease-ease);
-}
-
-/* Tooltip Bubble */
-.ease-tooltip::after {
- content: attr(data-tooltip);
- background-color: var(--ease-color-neutral-800);
- color: var(--ease-color-surface);
- padding: var(--ease-space-2) var(--ease-space-3);
- border-radius: var(--ease-radius-md);
- font-family: var(--ease-font-sans);
- font-size: var(--ease-text-xs);
+ z-index: var(--ease-z-toast, 9999);
+
+ /* Typography */
+ font-size: var(--tooltip-font-size);
font-weight: 500;
+ line-height: 1.4;
white-space: nowrap;
- box-shadow: var(--ease-shadow-md);
+ text-align: center;
+ max-width: var(--tooltip-max-width);
+
+ /* Appearance */
+ background: var(--tooltip-bg);
+ color: var(--tooltip-color);
+ padding: var(--tooltip-padding-y) var(--tooltip-padding-x);
+ border-radius: var(--tooltip-radius);
+
+ /* Hidden state */
+ opacity: 0;
+ pointer-events: none;
+
+ /* Transition */
+ transition:
+ opacity var(--tooltip-speed) var(--tooltip-ease),
+ transform var(--tooltip-speed) var(--tooltip-ease);
}
-/* Tooltip Arrow */
-.ease-tooltip::before {
+
+/* ── Arrow tip (::after) ───────────────────────────────────
+ Built with a zero-size box + border trick.
+ ──────────────────────────────────────────────────────────── */
+
+.tooltip::after {
content: '';
- border: 5px solid transparent;
+ position: absolute;
+ z-index: var(--ease-z-toast, 9999);
+ width: 0;
+ height: 0;
+ pointer-events: none;
+
+ /* Hidden state */
+ opacity: 0;
+ transition:
+ opacity var(--tooltip-speed) var(--tooltip-ease),
+ transform var(--tooltip-speed) var(--tooltip-ease);
}
-/* Hover & Focus States (Show Tooltip) */
-.ease-tooltip:hover::after,
-.ease-tooltip:hover::before,
-.ease-tooltip:focus-visible::after,
-.ease-tooltip:focus-visible::before {
- opacity: 1;
- visibility: visible;
+
+/* ================================================================
+ POSITION: TOP (default)
+ Bubble appears above the trigger, arrow points down.
+ ================================================================ */
+
+.tooltip,
+.tooltip-top {
+ --_enter-transform: translateX(-50%) translateY(0);
+ --_exit-transform: translateX(-50%) translateY(4px);
}
-/* ── Positioning Variants ──────────────────────────────────── */
+.tooltip::before,
+.tooltip-top::before {
+ bottom: calc(100% + var(--tooltip-offset));
+ left: 50%;
+ transform: var(--_exit-transform);
+}
-/* TOP (Default) */
-.ease-tooltip-top::after,
-.ease-tooltip:not([class*="ease-tooltip-"])::after {
- bottom: 100%;
- left: 50%;
- transform: translate(-50%, -4px);
+.tooltip::after,
+.tooltip-top::after {
+ bottom: calc(100% + var(--tooltip-offset) - var(--tooltip-arrow-size));
+ left: 50%;
+ transform: translateX(-50%) translateY(4px);
+ border-width: var(--tooltip-arrow-size) var(--tooltip-arrow-size) 0;
+ border-style: solid;
+ border-color: var(--tooltip-bg) transparent transparent;
}
-.ease-tooltip-top::before,
-.ease-tooltip:not([class*="ease-tooltip-"])::before {
- bottom: 100%;
- left: 50%;
- transform: translate(-50%, 6px);
- border-top-color: var(--ease-color-neutral-800);
+
+/* Visible state — top */
+.tooltip:hover::before,
+.tooltip:focus-within::before,
+.tooltip-top:hover::before,
+.tooltip-top:focus-within::before {
+ opacity: 1;
+ transform: var(--_enter-transform);
}
-.ease-tooltip-top:hover::after,
-.ease-tooltip:not([class*="ease-tooltip-"]):hover::after { transform: translate(-50%, -8px); }
-.ease-tooltip-top:hover::before,
-.ease-tooltip:not([class*="ease-tooltip-"]):hover::before { transform: translate(-50%, 2px); }
-/* BOTTOM */
-.ease-tooltip-bottom::after {
- top: 100%; left: 50%; transform: translate(-50%, 4px);
+.tooltip:hover::after,
+.tooltip:focus-within::after,
+.tooltip-top:hover::after,
+.tooltip-top:focus-within::after {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
}
-.ease-tooltip-bottom::before {
- top: 100%; left: 50%; transform: translate(-50%, -6px); border-bottom-color: var(--ease-color-neutral-800);
+
+
+/* ================================================================
+ POSITION: BOTTOM
+ Bubble appears below, arrow points up.
+ ================================================================ */
+
+.tooltip-bottom::before {
+ top: calc(100% + var(--tooltip-offset));
+ bottom: auto;
+ left: 50%;
+ transform: translateX(-50%) translateY(-4px);
}
-.ease-tooltip-bottom:hover::after { transform: translate(-50%, 8px); }
-.ease-tooltip-bottom:hover::before { transform: translate(-50%, -2px); }
-/* RIGHT */
-.ease-tooltip-right::after {
- top: 50%; left: 100%; transform: translate(4px, -50%);
+.tooltip-bottom::after {
+ top: calc(100% + var(--tooltip-offset) - var(--tooltip-arrow-size));
+ bottom: auto;
+ left: 50%;
+ transform: translateX(-50%) translateY(-4px);
+ border-width: 0 var(--tooltip-arrow-size) var(--tooltip-arrow-size);
+ border-style: solid;
+ border-color: transparent transparent var(--tooltip-bg);
}
-.ease-tooltip-right::before {
- top: 50%; left: 100%; transform: translate(-6px, -50%); border-right-color: var(--ease-color-neutral-800);
+
+.tooltip-bottom:hover::before,
+.tooltip-bottom:focus-within::before {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
}
-.ease-tooltip-right:hover::after { transform: translate(8px, -50%); }
-.ease-tooltip-right:hover::before { transform: translate(-2px, -50%); }
-/* LEFT */
-.ease-tooltip-left::after {
- top: 50%; right: 100%; transform: translate(-4px, -50%);
+.tooltip-bottom:hover::after,
+.tooltip-bottom:focus-within::after {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
}
-.ease-tooltip-left::before {
- top: 50%; right: 100%; transform: translate(6px, -50%); border-left-color: var(--ease-color-neutral-800);
+
+
+/* ================================================================
+ POSITION: LEFT
+ Bubble appears to the left, arrow points right.
+ ================================================================ */
+
+.tooltip-left::before {
+ right: calc(100% + var(--tooltip-offset));
+ left: auto;
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%) translateX(4px);
+}
+
+.tooltip-left::after {
+ right: calc(100% + var(--tooltip-offset) - var(--tooltip-arrow-size));
+ left: auto;
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%) translateX(4px);
+ border-width: var(--tooltip-arrow-size) 0 var(--tooltip-arrow-size) var(--tooltip-arrow-size);
+ border-style: solid;
+ border-color: transparent transparent transparent var(--tooltip-bg);
+}
+
+.tooltip-left:hover::before,
+.tooltip-left:focus-within::before {
+ opacity: 1;
+ transform: translateY(-50%) translateX(0);
+}
+
+.tooltip-left:hover::after,
+.tooltip-left:focus-within::after {
+ opacity: 1;
+ transform: translateY(-50%) translateX(0);
}
-.ease-tooltip-left:hover::after { transform: translate(-8px, -50%); }
-.ease-tooltip-left:hover::before { transform: translate(2px, -50%); }
-/* ── Accessibility: Reduced Motion ─────────────────────────── */
+/* ================================================================
+ POSITION: RIGHT
+ Bubble appears to the right, arrow points left.
+ ================================================================ */
+
+.tooltip-right::before {
+ left: calc(100% + var(--tooltip-offset));
+ right: auto;
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%) translateX(-4px);
+}
+
+.tooltip-right::after {
+ left: calc(100% + var(--tooltip-offset) - var(--tooltip-arrow-size));
+ right: auto;
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%) translateX(-4px);
+ border-width: var(--tooltip-arrow-size) var(--tooltip-arrow-size) var(--tooltip-arrow-size) 0;
+ border-style: solid;
+ border-color: transparent var(--tooltip-bg) transparent transparent;
+}
+
+.tooltip-right:hover::before,
+.tooltip-right:focus-within::before {
+ opacity: 1;
+ transform: translateY(-50%) translateX(0);
+}
+
+.tooltip-right:hover::after,
+.tooltip-right:focus-within::after {
+ opacity: 1;
+ transform: translateY(-50%) translateX(0);
+}
+
+
+/* ================================================================
+ ACCESSIBILITY: Reduced Motion
+ Remove transitions entirely — tooltip still shows/hides,
+ just without animation. Never hide information.
+ ================================================================ */
+
@media (prefers-reduced-motion: reduce) {
- .ease-tooltip::after,
- .ease-tooltip::before {
- /* Preserve opacity fades, but strip the sliding transforms */
- transition: opacity var(--ease-speed-fast) var(--ease-ease),
- visibility var(--ease-speed-fast) var(--ease-ease) !important;
+ .tooltip::before,
+ .tooltip::after,
+ .tooltip-top::before,
+ .tooltip-top::after,
+ .tooltip-bottom::before,
+ .tooltip-bottom::after,
+ .tooltip-left::before,
+ .tooltip-left::after,
+ .tooltip-right::before,
+ .tooltip-right::after {
+ transition: none !important;
}
-
- /* Reset hovers back to their original resting transforms, so they fade in place */
- .ease-tooltip-top:hover::after,
- .ease-tooltip:not([class*="ease-tooltip-"]):hover::after { transform: translate(-50%, -4px) !important; }
- .ease-tooltip-top:hover::before,
- .ease-tooltip:not([class*="ease-tooltip-"]):hover::before { transform: translate(-50%, 6px) !important; }
-
- .ease-tooltip-bottom:hover::after { transform: translate(-50%, 4px) !important; }
- .ease-tooltip-bottom:hover::before { transform: translate(-50%, -6px) !important; }
-
- .ease-tooltip-right:hover::after { transform: translate(4px, -50%) !important; }
- .ease-tooltip-right:hover::before { transform: translate(-6px, -50%) !important; }
-
- .ease-tooltip-left:hover::after { transform: translate(-4px, -50%) !important; }
- .ease-tooltip-left:hover::before { transform: translate(6px, -50%) !important; }
-}
+}
\ No newline at end of file