- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
đŻ The Adoption Opportunity
You've rolled out Now Assist, AI Agents, and Otto (Employee Slate). The platform team built it, leadership wants the ROI story, and the pieces are in place.
The teams that win this transformation all share one mindset:
"Let's make Otto the front door â and give people a clear path forward, no matter what they're trying to do."
That's the accelerator of AI ROI. Every great Otto rollout follows the same arc:
| Approach | What Happens |
|---|---|
| Keep classic ESC as default | Safe, familiar â but the AI investment waits to shine |
| Switch to Otto with no safety net | Bold and fast â works when every flow is covered |
| Switch to Otto + a graceful bridge | Users explore with confidence â Every bridge click becomes a signal â Process team builds the missing use cases â Otto gets smarter every week |
It's not "Otto or ESC" â it's "Otto, with a bridge back when needed." You only need one small widget to unlock it.
đĄ The Widget
A Lit-based AIUX widget that:
- ⨠Floats on every page in Employee Slate
- đŻ Drags anywhere â snaps to the nearest edge on release (iOS AssistiveTouch-style)
- đž Remembers position during the session
- âŞď¸ Navigates to
/escin the same tab - đĄ Fires a
esc-footer-clickevent for analytics & surveys
Why this matters: Every click is a high-value signal â "This user was in Otto, and at this moment, they chose to go elsewhere." That's the most valuable input your AI roadmap can have.
âď¸ The Build
Step 1: Create the widget
Navigate to sys_aix_widget.list â New. Fill in:
| Name | ESC Footer Widget |
| ID | esc-footer-widget |
| Category | layout |
| Input Schema | { "label": { "type": "String" }, "url": { "type": "String" } } |
Step 2: Paste the Component code
import { html, css } from 'lit';
import { AIUXWidgetElement } from '@servicenow/aiux-components-core';
class EscFooterWidget extends AIUXWidgetElement {
static properties = {
label: { type: String },
url: { type: String },
};
constructor() {
super();
this.label = 'Switch to Employee Center';
this.url = '/esc';
this._dragStartX = 0;
this._dragStartY = 0;
this._buttonStartX = 0;
this._buttonStartY = 0;
this._currentX = 0;
this._currentY = 0;
this._movedDistance = 0;
this._isDragging = false;
this._dragThreshold = 5;
this._boundPointerMove = this._handlePointerMove.bind(this);
this._boundPointerUp = this._handlePointerUp.bind(this);
}
firstUpdated() {
this._btn = this.renderRoot.querySelector('.esc-fab');
const labelEl = this.renderRoot.querySelector('.esc-fab__label');
if (this._btn) {
this._btn.setAttribute('aria-label', this.label);
this._btn.addEventListener('pointerdown', this._handlePointerDown.bind(this));
this._btn.addEventListener('click', this._handleClick.bind(this));
}
if (labelEl) labelEl.textContent = this.label;
this._restorePosition();
}
_restorePosition() {
try {
const saved = sessionStorage.getItem('escFabPosition');
if (saved) {
const { x, y } = JSON.parse(saved);
this._currentX = x;
this._currentY = y;
this._applyPosition(x, y);
}
} catch (e) {}
}
_savePosition(x, y) {
try {
sessionStorage.setItem('escFabPosition', JSON.stringify({ x, y }));
} catch (e) {}
}
_applyPosition(x, y) {
if (!this._btn) return;
this._btn.style.left = x + 'px';
this._btn.style.top = y + 'px';
this._btn.style.right = 'auto';
this._btn.style.bottom = 'auto';
}
_handlePointerDown(event) {
if (event.button !== undefined && event.button !== 0) return;
event.preventDefault();
this._btn.setPointerCapture(event.pointerId);
const rect = this._btn.getBoundingClientRect();
this._dragStartX = event.clientX;
this._dragStartY = event.clientY;
this._buttonStartX = rect.left;
this._buttonStartY = rect.top;
this._movedDistance = 0;
this._isDragging = false;
this._btn.classList.remove('is-snapping');
document.addEventListener('pointermove', this._boundPointerMove);
document.addEventListener('pointerup', this._boundPointerUp);
}
_handlePointerMove(event) {
const dx = event.clientX - this._dragStartX;
const dy = event.clientY - this._dragStartY;
this._movedDistance = Math.sqrt(dx * dx + dy * dy);
if (!this._isDragging && this._movedDistance > this._dragThreshold) {
this._isDragging = true;
this._btn.classList.add('is-dragging');
}
if (this._isDragging) {
const padding = 8;
const btnWidth = this._btn.offsetWidth;
const btnHeight = this._btn.offsetHeight;
const maxX = window.innerWidth - btnWidth - padding;
const maxY = window.innerHeight - btnHeight - padding;
let newX = this._buttonStartX + dx;
let newY = this._buttonStartY + dy;
newX = Math.max(padding, Math.min(newX, maxX));
newY = Math.max(padding, Math.min(newY, maxY));
this._currentX = newX;
this._currentY = newY;
this._applyPosition(newX, newY);
}
}
_handlePointerUp(event) {
document.removeEventListener('pointermove', this._boundPointerMove);
document.removeEventListener('pointerup', this._boundPointerUp);
if (this._isDragging) this._snapToEdge();
}
_snapToEdge() {
const padding = 16;
const btnWidth = this._btn.offsetWidth;
const viewportCenter = window.innerWidth / 2;
const btnCenter = this._currentX + (btnWidth / 2);
const snapX = btnCenter < viewportCenter
? padding
: window.innerWidth - btnWidth - padding;
this._btn.classList.remove('is-dragging');
this._btn.classList.add('is-snapping');
this._currentX = snapX;
this._applyPosition(snapX, this._currentY);
this._savePosition(snapX, this._currentY);
setTimeout(() => {
this._btn.classList.remove('is-snapping');
this._isDragging = false;
}, 350);
}
_handleClick(event) {
event.preventDefault();
event.stopPropagation();
if (this._movedDistance > this._dragThreshold) return;
this.dispatchEvent(new CustomEvent('esc-footer-click', {
bubbles: true,
composed: true,
detail: { url: this.url, timestamp: new Date().toISOString() },
}));
window.location.assign(this.url);
}
render() {
return html`
<div class="esc-fab" role="button" tabindex="0">
<span class="esc-fab__label"></span>
<svg class="esc-fab__icon" xmlns="http://www.w3.org/2000/svg"
width="12" height="12" viewBox="0 0 20 20" aria-hidden="true">
<path fill="currentColor"
d="M16.5 3a.5.5 0 0 1 .5.5v10a.5.5 0 0 1-1 0V4.707L3.854 16.853a.5.5 0 0 1-.707-.707L15.293 4H6.5a.5.5 0 0 1 0-1h10Z"/>
</svg>
</div>
`;
}
static styles = css`
:host {
display: contents;
font-family: var(--font-sans, Inter, "Servicenow Sans", sans-serif);
}
.esc-fab {
position: fixed; bottom: 24px; right: 24px; z-index: 9999;
display: inline-flex; align-items: center;
gap: calc(var(--spacing, 0.25rem) * 1);
padding: calc(var(--spacing, 0.25rem) * 1.5) calc(var(--spacing, 0.25rem) * 3.5);
background: var(--color-base-100, #ffffff);
color: var(--color-base-content, #1e293b);
border: 1px solid var(--color-base-300, #e5e5e5);
border-radius: var(--radius-full, 9999px);
font-size: var(--text-xs, 0.75rem);
font-weight: var(--font-weight-medium, 500);
cursor: grab; white-space: nowrap;
user-select: none; touch-action: none; -webkit-user-select: none;
box-shadow: 0 3px 8px rgba(0,0,0,0.07), 0 1px 3px rgba(0,0,0,0.04);
transition: box-shadow 0.2s ease, background 0.2s ease;
}
.esc-fab:hover:not(.is-dragging):not(.is-snapping) {
box-shadow: 0 8px 20px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.06);
}
.esc-fab.is-dragging {
cursor: grabbing; transition: none;
transform: scale(1.05);
box-shadow: 0 16px 32px rgba(0,0,0,0.20), 0 8px 12px rgba(0,0,0,0.12);
opacity: 0.95;
border-color: var(--color-primary, #003B54);
}
.esc-fab.is-snapping {
transition: left 0.35s cubic-bezier(0.22, 1, 0.36, 1),
top 0.35s cubic-bezier(0.22, 1, 0.36, 1),
transform 0.35s ease, box-shadow 0.35s ease;
transform: scale(1);
}
.esc-fab:focus-visible {
outline: 2px solid var(--color-primary, #003B54);
outline-offset: 3px;
}
.esc-fab__icon {
display: inline-block; flex-shrink: 0;
opacity: 0.7; transition: opacity 0.2s ease; pointer-events: none;
}
.esc-fab__label { pointer-events: none; }
.esc-fab:hover:not(.is-dragging) .esc-fab__icon { opacity: 1; }
@media (max-width: 480px) {
.esc-fab {
bottom: 16px; right: 16px;
padding: calc(var(--spacing, 0.25rem) * 1) calc(var(--spacing, 0.25rem) * 2.5);
}
}
@media (prefers-reduced-motion: reduce) {
.esc-fab, .esc-fab__icon, .esc-fab.is-snapping { transition: none; }
.esc-fab.is-dragging { transform: none; }
}
`;
}
Step 3: Bind it to Employee Slate
- Navigate to your default experience in
sys_aix_experience - Open the App Shell record mapped to that experience in
sys_aix_app_shell - Add
esc-footer-widgetto the Footer field
That's it â refresh Employee Slate and the floating bridge is live on every page.
đĄ Capturing the Signal
Listen for the bridge event anywhere on the page:
document.addEventListener('esc-footer-click', (e) => {
GlideAjax.send('fallback_capture_api', {
timestamp: e.detail.timestamp,
page: window.location.pathname,
user: window.NOW.user.userID
});
});
From there, route to your survey, your analytics dashboard, or both.
đ Closing the Loop
- Build a "Bridge Insights" dashboard â group bridge clicks by page, role, and time
- Auto-trigger a 2-question survey: "What were you trying to do?"
- Feed responses into your AI use-case backlog â pre-validated by real users
- Track the bridge rate over time â watch it decline as you ship the missing flows
đ Three months in, you'll be able to show leadership exactly which AI investments delivered â by pointing to the use cases that moved from "bridge hot spots" to "fully Otto-handled." That's the ROI story that funds the next investment.
đŹ Let's Discuss
- đ¤ AEs/CSEs: What's working for your customers in their Otto rollouts?
- đ ď¸ Developers: Built something similar? Share your pattern.
- đ AI/Process leads: How are you identifying new AI use cases today?
If this gave you an idea you can use, hit đ and share it with your customer success team. Every team that ships this pattern shortens the road to Otto adoption.
About the Author: Senior Technical Support Engineer at ServiceNow on the ArcX team. CSA, CAD, CIS-HRSD, and ServiceNow Architecture Excellence certified. Two-time LLAMA Award recipient.
#NowAssist #EmployeeSlate #Otto #AIAdoption #LitWebComponents #ServiceNowDeveloper #AIUX
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
