{"id":633,"date":"2026-03-30T13:00:00","date_gmt":"2026-03-30T18:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=633"},"modified":"2026-03-07T03:33:09","modified_gmt":"2026-03-07T08:33:09","slug":"custom-smart-banners","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/custom-smart-banners\/","title":{"rendered":"Custom Smart Banners: Design and Implementation Guide"},"content":{"rendered":"\n<p>Smart banners are small, dismissible strips at the top or bottom of a web page that invite mobile visitors to open or install your app. The <a href=\"https:\/\/developer.apple.com\/documentation\/webkit\/promoting-apps-with-smart-app-banners\" rel=\"nofollow noopener\" target=\"_blank\">Apple meta tag approach<\/a> has been around since iOS 6, but it gives you almost no control. You get a fixed design, no analytics, and no ability to target specific user segments.<\/p>\n\n\n\n<p>Custom smart banners fix all of that. By building your own, you control exactly how the banner looks, when it appears, how long it waits, and where it sends users. This guide covers the full implementation: HTML structure, CSS styling, JavaScript behavior, responsive layout, animation, and integration with <a href=\"https:\/\/tolinku.com\/features\/smart-banners\">Tolinku&#39;s smart banner platform<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1080\" height=\"720\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/custom-smart-banners-inline-0.jpg\" alt=\"a man sitting in front of a laptop computer\" class=\"wp-image-504\" srcset=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/custom-smart-banners-inline-0.jpg 1080w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/custom-smart-banners-inline-0-300x200.jpg 300w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/custom-smart-banners-inline-0-1024x683.jpg 1024w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/custom-smart-banners-inline-0-768x512.jpg 768w\" sizes=\"auto, (max-width: 1080px) 100vw, 1080px\" \/><figcaption class=\"wp-element-caption\">Photo by <a href=\"https:\/\/unsplash.com\/@stoman?utm_source=tolinku&#038;utm_medium=referral\" rel=\"nofollow noopener\" target=\"_blank\">Nangialai Stoman<\/a> on <a href=\"https:\/\/unsplash.com\/?utm_source=tolinku&#038;utm_medium=referral\" rel=\"nofollow noopener\" target=\"_blank\">Unsplash<\/a><\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The Core HTML Structure<\/h2>\n\n\n\n<p>A smart banner needs to be lightweight. It should load fast, sit above your page content, and not cause layout shifts that hurt your <a href=\"https:\/\/web.dev\/articles\/vitals\" rel=\"nofollow noopener\" target=\"_blank\">Core Web Vitals<\/a> scores. Here is a solid starting structure:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;div id=&quot;tolk-banner&quot; class=&quot;tolk-banner tolk-banner--top&quot; role=&quot;banner&quot; aria-label=&quot;Download our app&quot;&gt;\n  &lt;div class=&quot;tolk-banner__inner&quot;&gt;\n    &lt;button class=&quot;tolk-banner__close&quot; aria-label=&quot;Dismiss banner&quot; type=&quot;button&quot;&gt;\n      &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; fill=&quot;none&quot; aria-hidden=&quot;true&quot;&gt;\n        &lt;path d=&quot;M12 4L4 12M4 4l8 8&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot;\/&gt;\n      &lt;\/svg&gt;\n    &lt;\/button&gt;\n    &lt;img class=&quot;tolk-banner__icon&quot; src=&quot;\/images\/app-icon.png&quot; alt=&quot;&quot; width=&quot;40&quot; height=&quot;40&quot; loading=&quot;lazy&quot;&gt;\n    &lt;div class=&quot;tolk-banner__text&quot;&gt;\n      &lt;span class=&quot;tolk-banner__title&quot;&gt;MyApp&lt;\/span&gt;\n      &lt;span class=&quot;tolk-banner__subtitle&quot;&gt;Open in the app for a better experience&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;a class=&quot;tolk-banner__cta&quot; href=&quot;#&quot; id=&quot;tolk-banner-link&quot;&gt;Open App&lt;\/a&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;\n<\/code><\/pre>\n\n\n\n<p>A few things matter here. The <code>role=&quot;banner&quot;<\/code> attribute helps screen readers understand the element&#39;s purpose. The close button has an <code>aria-label<\/code> so keyboard users know what it does. The app icon uses <code>loading=&quot;lazy&quot;<\/code> because it is not critical for the initial render.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CSS: Mobile-First Layout<\/h2>\n\n\n\n<p>The banner should render cleanly on a 320px screen first, then adapt upward. Avoid fixed widths. Use <code>safe-area-inset-*<\/code> CSS environment variables for devices with notches or home indicators:<\/p>\n\n\n\n<pre><code class=\"language-css\">.tolk-banner {\n  position: fixed;\n  left: 0;\n  right: 0;\n  z-index: 9999;\n  background: #ffffff;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n  padding: env(safe-area-inset-top, 0) env(safe-area-inset-right, 0) 0 env(safe-area-inset-left, 0);\n  transform: translateY(-100%);\n  transition: transform 0.3s ease;\n}\n\n.tolk-banner--top {\n  top: 0;\n}\n\n.tolk-banner--bottom {\n  bottom: 0;\n  top: auto;\n  transform: translateY(100%);\n  padding: 0 env(safe-area-inset-right, 0) env(safe-area-inset-bottom, 0) env(safe-area-inset-left, 0);\n  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.12);\n}\n\n.tolk-banner--visible {\n  transform: translateY(0);\n}\n\n.tolk-banner__inner {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  padding: 10px 14px;\n  max-width: 680px;\n  margin: 0 auto;\n}\n\n.tolk-banner__icon {\n  width: 40px;\n  height: 40px;\n  border-radius: 9px;\n  flex-shrink: 0;\n}\n\n.tolk-banner__text {\n  flex: 1;\n  min-width: 0;\n  display: flex;\n  flex-direction: column;\n}\n\n.tolk-banner__title {\n  font-weight: 600;\n  font-size: 14px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.tolk-banner__subtitle {\n  font-size: 12px;\n  color: #666;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.tolk-banner__cta {\n  background: #007aff;\n  color: #fff;\n  border-radius: 20px;\n  padding: 7px 16px;\n  font-size: 13px;\n  font-weight: 600;\n  text-decoration: none;\n  white-space: nowrap;\n  flex-shrink: 0;\n}\n\n.tolk-banner__close {\n  background: none;\n  border: none;\n  cursor: pointer;\n  color: #999;\n  padding: 4px;\n  flex-shrink: 0;\n  line-height: 0;\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>transform: translateY(-100%)<\/code> default state keeps the banner off screen until you add the <code>tolk-banner--visible<\/code> class. This approach avoids layout shifts because the banner starts outside the viewport rather than collapsing in the document flow.<\/p>\n\n\n\n<p>Using <code>env(safe-area-inset-*)<\/code> is important for iPhones with a notch or Dynamic Island. Without it, the banner content can sit under the status bar. The <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/env\" rel=\"nofollow noopener\" target=\"_blank\">CSS environment variables spec<\/a> is well-supported across all modern mobile browsers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JavaScript: Show, Dismiss, and Persist Decisions<\/h2>\n\n\n\n<p>The JavaScript layer handles three things: deciding whether to show the banner, building the correct deep link URL, and persisting the dismiss state so the banner does not reappear immediately.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">(function () {\n  const STORAGE_KEY = &#39;tolk_banner_dismissed&#39;;\n  const DISMISS_DURATION_DAYS = 7;\n\n  function isDismissed() {\n    try {\n      const raw = localStorage.getItem(STORAGE_KEY);\n      if (!raw) return false;\n      const { timestamp } = JSON.parse(raw);\n      const age = Date.now() - timestamp;\n      return age &lt; DISMISS_DURATION_DAYS * 24 * 60 * 60 * 1000;\n    } catch {\n      return false;\n    }\n  }\n\n  function dismiss() {\n    try {\n      localStorage.setItem(STORAGE_KEY, JSON.stringify({ timestamp: Date.now() }));\n    } catch {}\n    const banner = document.getElementById(&#39;tolk-banner&#39;);\n    if (banner) {\n      banner.classList.remove(&#39;tolk-banner--visible&#39;);\n      setTimeout(() =&gt; banner.remove(), 300);\n    }\n  }\n\n  function isMobile() {\n    return \/Android|iPhone|iPad|iPod\/i.test(navigator.userAgent);\n  }\n\n  function init() {\n    if (!isMobile()) return;\n    if (isDismissed()) return;\n\n    const banner = document.getElementById(&#39;tolk-banner&#39;);\n    if (!banner) return;\n\n    \/\/ Wire up close button\n    const closeBtn = banner.querySelector(&#39;.tolk-banner__close&#39;);\n    if (closeBtn) closeBtn.addEventListener(&#39;click&#39;, dismiss);\n\n    \/\/ Show the banner after a short delay\n    setTimeout(() =&gt; {\n      banner.classList.add(&#39;tolk-banner--visible&#39;);\n    }, 1500);\n  }\n\n  if (document.readyState === &#39;loading&#39;) {\n    document.addEventListener(&#39;DOMContentLoaded&#39;, init);\n  } else {\n    init();\n  }\n})();\n<\/code><\/pre>\n\n\n\n<p>The <code>isMobile()<\/code> check prevents the banner from appearing on desktop browsers where it would not be useful. The dismiss state is stored in <code>localStorage<\/code> with a timestamp so users who dismissed the banner seven days ago see it again. You can tune that duration based on your audience.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Integrating the Tolinku SDK<\/h2>\n\n\n\n<p>The Tolinku SDK handles the most important part: generating the correct deep link for each platform and tracking the click. Without attribution, you cannot tell whether the banner is actually driving installs.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/platform-platform-banners.png\" alt=\"Tolinku smart banner configuration in the dashboard\"><\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;script src=&quot;https:\/\/cdn.tolinku.com\/sdk\/v1\/banner.js&quot; async&gt;&lt;\/script&gt;\n<\/code><\/pre>\n\n\n\n<p>Once the SDK loads, update the CTA link dynamically:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">window.tolinkuReady = function (tolinku) {\n  const link = tolinku.createLink({\n    campaign: &#39;smart-banner&#39;,\n    channel: &#39;web&#39;,\n    feature: &#39;banner&#39;,\n    data: {\n      \/\/ Pass the current page path so the app can open the right screen\n      $deeplink_path: window.location.pathname,\n    },\n  });\n\n  const cta = document.getElementById(&#39;tolk-banner-link&#39;);\n  if (cta) cta.href = link;\n};\n<\/code><\/pre>\n\n\n\n<p>The SDK detects whether the user is on iOS or Android and routes them to the correct store if the app is not installed, or opens the app directly if it is. The <code>$deeplink_path<\/code> parameter carries the current page context into the app so users land in the right place rather than the app&#39;s home screen. Read more in the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/\">Tolinku smart banners documentation<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Responsive Design Considerations<\/h2>\n\n\n\n<p>On very small screens (under 360px), the subtitle and app name can overlap the CTA button. Add a media query to hide the subtitle in that case:<\/p>\n\n\n\n<pre><code class=\"language-css\">@media (max-width: 359px) {\n  .tolk-banner__subtitle {\n    display: none;\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>For tablets, you might not want the banner at all. Tablets often have better screen real estate for web content, and users are less likely to prefer an app experience on a 10-inch screen. Add a tablet exclusion to your <code>isMobile()<\/code> check:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">function isMobile() {\n  const ua = navigator.userAgent;\n  if (\/iPad\/i.test(ua)) return false; \/\/ exclude iPads\n  return \/Android|iPhone|iPod\/i.test(ua);\n}\n<\/code><\/pre>\n\n\n\n<p>This respects the <a href=\"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/bars\/navigation-bars\/\" rel=\"nofollow noopener\" target=\"_blank\">Apple Human Interface Guidelines<\/a> principle of not interrupting tablet users with mobile-oriented prompts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Animation and Performance<\/h2>\n\n\n\n<p>The slide-in animation uses CSS transforms, which run on the compositor thread and avoid triggering layout or paint. Never animate <code>top<\/code>, <code>height<\/code>, or <code>margin<\/code> for a banner. Those properties force the browser to recalculate layout on every frame, which tanks performance on mid-range Android devices.<\/p>\n\n\n\n<p>If you want a fade combined with the slide:<\/p>\n\n\n\n<pre><code class=\"language-css\">.tolk-banner {\n  opacity: 0;\n  transform: translateY(-100%);\n  transition: transform 0.3s ease, opacity 0.3s ease;\n}\n\n.tolk-banner--visible {\n  opacity: 1;\n  transform: translateY(0);\n}\n<\/code><\/pre>\n\n\n\n<p>Avoid <code>will-change: transform<\/code> unless you have confirmed a performance issue. Overusing <code>will-change<\/code> promotes every element to its own compositor layer, which increases memory usage on mobile.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Positioning: Top vs. Bottom<\/h2>\n\n\n\n<p>Top banners are the convention on iOS, matching where Safari&#39;s address bar used to live. Bottom banners are more thumb-friendly on tall phones, and they do not collide with the iOS Dynamic Island or Android status bar notifications.<\/p>\n\n\n\n<p>If you go with a bottom banner, invert the transform:<\/p>\n\n\n\n<pre><code class=\"language-css\">.tolk-banner--bottom {\n  bottom: 0;\n  top: auto;\n  transform: translateY(100%);\n}\n<\/code><\/pre>\n\n\n\n<p>The <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/display-behavior\/\">Tolinku display behavior documentation<\/a> covers how to configure position and timing without writing custom code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Connecting Design to Targeting<\/h2>\n\n\n\n<p>A custom banner is more useful when it knows who it is talking to. Tolinku&#39;s <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/audiences\/\">audience targeting<\/a> lets you show different banners to different users based on OS version, browser, geography, or custom attributes. You can build the banner in HTML\/CSS as described here and then use Tolinku&#39;s configuration layer to control which users see it and when.<\/p>\n\n\n\n<p>For example, you might show a banner with &quot;Update your app&quot; copy to existing users who have an older app version, and &quot;Get the app&quot; copy to first-time visitors. Both banners share the same HTML\/CSS structure but pull different copy from the Tolinku dashboard.<\/p>\n\n\n\n<p>See the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/smart-banners\/targeting-and-scheduling\/\">targeting and scheduling documentation<\/a> for the full rule syntax.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Your Banner<\/h2>\n\n\n\n<p>Before deploying, test on real devices. Chrome DevTools device emulation is useful for layout but does not accurately simulate mobile browser chrome or safe area insets.<\/p>\n\n\n\n<p>Checklist:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Banner appears within 1-2 seconds on a mid-range Android (not just a flagship)<\/li>\n<li>Banner does not cause a layout shift that moves page content<\/li>\n<li>Dismiss persists across page navigation<\/li>\n<li>CTA link opens the correct app store on iOS and Android separately<\/li>\n<li>Banner is readable at the smallest supported viewport width<\/li>\n<li>Screen reader announces the banner and the dismiss button correctly<\/li>\n<li>Banner does not appear on desktop or tablet<\/li>\n<\/ul>\n\n\n\n<p>The <a href=\"https:\/\/tolinku.com\/features\/smart-banners\">Tolinku features page<\/a> shows a live preview tool you can use to check banner designs before publishing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building a custom smart banner from scratch gives you full control over design, behavior, and integration. This guide walks through HTML, CSS, JavaScript, and Tolinku SDK setup so your banners work perfectly across every device.<\/p>\n","protected":false},"author":2,"featured_media":632,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Custom Smart Banners: Design & Implementation Guide","rank_math_description":"Learn how to build custom smart banners with HTML, CSS, and JavaScript. Covers responsive design, animation, dismiss behavior, and Tolinku SDK integration.","rank_math_focus_keyword":"custom smart banners","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-custom-smart-banners.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-custom-smart-banners.png","footnotes":""},"categories":[16],"tags":[131,71,69,40,41],"class_list":["post-633","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-marketing","tag-implementation","tag-javascript","tag-mobile-development","tag-smart-banners","tag-web-to-app"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/633","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=633"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/633\/revisions"}],"predecessor-version":[{"id":2113,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/633\/revisions\/2113"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/632"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}