Financial Management Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
Yogananda
Product and Topic Expert
Product and Topic Expert
480
The holiday season is here, and what better way to spread festive cheer than by giving your SAP CPQ tenant a beautiful Christmas makeover? A themed interface not only adds a touch of joy but also creates a delightful experience for your users during this special time of year.

Why Customize Your SAP CPQ Theme?

SAP CPQ offers flexible branding options that allow you to personalize the look and feel of your tenant. By applying a Christmas theme, you can:

  • Enhance user engagement with a festive interface.
  • Reflect your company’s holiday spirit.
  • Create a memorable experience for customers and internal teams.

Steps to Create a Christmas Theme in SAP CPQ

2025-12-09_19-30-46 (1).gif

1. Access Branding Settings

Navigate to Setup â†’ Administration → UI Design→Branding in your SAP CPQ tenant. This is where you can customize with your style sheet

/* --- Header Background Styling --- */
.christmas-theme {
    /* Set a dark background if desired, or keep original. */
    /* background-color: #0d1a26; */ 
    position: relative; /* Essential for positioning lights and snow */
    overflow: hidden; /* Important to keep snow/lights contained */
}

/* --- Christmas Lights Container --- */
.christmas-lights {
    top: 0;
    left: 0;
    width: 100%;
    height: 20px; /* Adjust height for visibility */
    display: flex;
    justify-content: space-around; /* Distribute lights evenly */
    align-items: center;
    pointer-events: none; /* Allows clicks to pass through */
    z-index: 10; /* Make sure lights are on top of the header background */
}

/* --- Light Bulb Styling --- */
.light-bulb {
    width: 11px;
    height: 15px;
    border-radius: 50% / 80% 80% 20% 20%; /* Bulb shape */
    box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
    animation: blink 1s infinite alternate; /* Simple blinking animation */
    position: relative;
    margin: 0 5px; /* Spacing between bulbs */
}

/* Add a "wire" to the bulb */
.light-bulb::before {
    content: '';
    position: absolute;
    top: -5px; /* Position above the bulb */
    left: 50%;
    transform: translateX(-50%);
    width: 5px;
    height: 5px;
    background-color: #333; /* Dark color for the wire */
}

/* --- Color Variations and Light Glow --- */
.light-bulb.red {
    background-color: #ff0000;
    box-shadow: 0 0 10px 3px #ff0000;
}
.light-bulb.green {
    background-color: #94BF31;
    box-shadow: 0 0 10px 3px #94BF31;
    animation-delay: 0.3s; /* Offset for blinking effect */
}
.light-bulb.gold {
    background-color: #00ff34;
    box-shadow: 0 0 10px 3px #00ff34;
    animation-delay: 0.6s; /* Offset for blinking effect */
}
.light-bulb.purple {
    background-color: #FF00FF;
    box-shadow: 0 0 10px 3px #FF00FF;
    animation-delay: 0.6s; /* Offset for blinking effect */
}
.light-bulb.blue {
    background-color: #0000FF;
    box-shadow: 0 0 10px 3px #0000FF;
    animation-delay: 0.6s; /* Offset for blinking effect */
}
.light-bulb.pink {
    background-color: #ff347b;
    box-shadow: 0 0 10px 3px #ff347b;
    animation-delay: 0.6s; /* Offset for blinking effect */
}

/* Blinking Animation */
@keyframes blink {
    from { opacity: 1; }
    to { opacity: 0.5; }
}

/* --- Snowfall Effect Container --- */
.snowfall-effect {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    background-image: radial-gradient(white 1px, transparent 0),
                      radial-gradient(white 2px, transparent 0),
                      radial-gradient(white 3px, transparent 0);
    background-size: 50px 50px, 70px 70px, 90px 90px;
    animation: snowfall 10s linear infinite; /* Animation to move the snow down */
    z-index: 5; /* Below the lights */
}

/* Snowfall Animation */
@keyframes snowfall {
    from { background-position: 0 0, 0 0, 0 0; }
    to { background-position: 500px 500px, 500px 500px, 500px 500px; }
}

Navigate to Setup→Administration → UI Design→Responsive Template in your SAP CPQ tenant. This is where you can customize with your knockoutJS template

@using WebSite.Controllers
<header role="banner">
    @*don't show header if user doesn't want menu (e.g. user came from mobile)*@
    <div id="headerContainer" class="row cald_header_bar hidden" data-bind="css: {'hidden': shouldHideMenu, 'wrap-when-landing-C4C': !landedFromC4C }">
        	<div class="row sap_header_bar flex flex-between">
            <div class="cald_header_section">
                <h1 class="cald_logo">@Translation.Get("MainMenu.MainHeading")</h1>
            </div>
            <div class="cald_header_section-right sap_header_section-right">
                <nav role="navigation" class="shellbar-navigation" aria-label="Primary Navigation">
                    <ul class="nav flex">
                        <li class="shellbar-navigation-item hidden" data-bind="css: { hidden : isElectronicallyGuestLoggedIn }"><a aria-label="@Translation.GetJS("quotation.ProposalMain.NewProject")" href="#" data-bind="click: createNewCart, tooltip: { title: '@Translation.GetJS("quotation.ProposalMain.NewProject")', placement: 'left', viewport: 'header' }" class="fiori3-create-new-quote-icon create-new-quote-icon nav-menu-icon">&#xe12b;</a></li>
                        @Html.PartialWithNameAsComment("RecentlyVisitedObjects")
                        @Html.PartialWithNameAsComment("CartSummary", true)
                        <li id="cpq-companion" data-bind="css: { hidden : !isEnabledSapCompanionFunctionality }"><a aria-label="@Translation.GetJS("Header.Companion.Notifications")" href="#" data-bind="tooltip: { title: '@Translation.GetJS("Header.Companion.Notifications")', placement: 'left', viewport: 'header' }" class="nav-menu-icon cpq-notifications-icon-style">&#xe239;</a></li>
                        @*show user menu*@
                        @*Show user menu only if user is not guest user nor electronically signed guest user*@
                        @Html.PartialWithNameAsComment("UserMenu")
                    </ul>
                </nav>
            </div>
        </div>
        <div>
            <button type="button" class="navbar-toggle pull-left" data-toggle="collapse" data-target=".navbar-collapse1">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
        </div>
        <div class="visible-xs logo-container">
            <div class="cald_logo no-margin"></div>
        </div>
        <!-- /.navbar-collapse -->
    </div>
            <div class="christmas-lights">
                <div class="light-bulb red"></div>
                <div class="light-bulb green"></div>
                <div class="light-bulb blue"></div>
                <div class="light-bulb purple"></div>
                <div class="light-bulb gold"></div>
                <div class="light-bulb pink"></div>
                <div class="light-bulb red"></div>
                <div class="light-bulb green"></div>
                <div class="light-bulb gold"></div>
                <div class="light-bulb blue"></div>
                <div class="light-bulb purple"></div>
                <div class="light-bulb red"></div>
                <div class="light-bulb pink"></div>
                <div class="light-bulb green"></div>
                <div class="light-bulb gold"></div>
                <div class="light-bulb blue"></div>
                <div class="light-bulb purple"></div>
                <div class="light-bulb blue"></div>
                <div class="light-bulb red"></div>
                <div class="light-bulb pink"></div>
            </div>
        <div class="snowfall-effect"></div>    
    <div id="topMenuC4C" class="hidden" data-bind="css: { hidden : !landedFromC4C }">
        <ul>
            <li class="main-list-item hidden @(@ViewBag.CurrentPage == "Catalog" ? "active" : "")" data-bind="css: {'hidden': landedFromC4C && !isQuoteLoaded}">
                <a href="/Catalogue/CategoryTree.aspx" class="sap-icon full-width" data-bind="tooltip: { title: '@Translation.GetJS("View.ResponsiveLayout.Catalog")', placement: 'left', viewport: 'header' }">
                    &#xe011;
                </a>
            </li>
            <li class="main-list-item" data-bind="css: { active: document.location.pathname == '/quotation/BulkValidation.aspx', 'hidden': !bulkValidationShow || useNewQuoteEngine }">
                <a href="/quotation/BulkValidation.aspx" class="sap-icon full-width" data-bind="tooltip: { title: '@Translation.GetJS("BulkValidation.Title")', placement: 'left', viewport: 'header' }">
                    &#xe059;
                </a>
            </li>
            <!-- ko if: isQuoteEnvironmentExists()-->
            @Html.PartialWithNameAsComment("CartSummary", false) @*don't show user menu*@
            <!-- /ko -->
            <li class="main-list-item">
                <a class="dropdown-toggle sap-icon full-width" data-toggle="dropdown" href="#" data-bind="tooltip: { title: '@Translation.GetJS("Menu.Setup")', placement: 'left', viewport: 'header' }">&#xe00c;</a>
                <ul class="left slide-out-section dropdown-menu slide-down side-menu-expanded-section setup-menu-dropdown-c4c" role="menu">
                    <li class="hidden" data-bind="if: setupLinksVisibility.isSetupLinkVisible(), css: { hidden : !tryIsAdmin }">
                        <a href="/admin/AdminTransfer.aspx?Log=1">
                            @Translation.Get("Menu.Setup")
                        </a>
                    </li>
                    <li class="hidden" data-bind="if: setupLinksVisibility.isDevelopConsoleLinkVisible(), css: { hidden : !tryIsAdmin }">
                        <a href="/DeveloperConsole" target="_blank">
                            @Translation.Get("Menu.DeveloperConsole")
                        </a>
                    </li>
                    <li class="hidden" data-bind="click: clearSessionStorageOnButtonClick, css: { hidden : !tryIsAdmin }"><a href="/multiusers/UserPersonalizationPage.aspx">@Translation.Get("Default.master.UserPage")</a></li>
                    <li class="hidden" data-bind="css: { hidden : !tryIsAdmin }"><a href="/multiusers/QuoteLayout.aspx">@Translation.Get("multiusers.QuoteLayout")</a></li>
                    <li class="hidden" data-bind="if: setupLinksVisibility.isEventLogLinkVisible(), css: { hidden : !tryIsAdmin }">
                        <a href="/SetupSpa/developer-tools/event-log" target="_blank">@Translation.Get("xmlEventLog")</a>
                    </li>
                    <li class="hidden" data-bind="click: clearSessionStorageOnButtonClick, css: { hidden : tryIsAdmin }"><a href="/multiusers/UserPersonalizationPage.aspx">@Translation.Get("Default.master.UserPage")</a></li>
                    <li class="hidden" data-bind="css: { hidden : tryIsAdmin }"><a href="/multiusers/QuoteLayout.aspx">@Translation.Get("multiusers.QuoteLayout")</a></li>
                </ul>
            </li>

        </ul>
    </div>
</header>
<div id="sideMenuContainer" data-bind="visible: isInitialized, css: (!landedFromC4C ? '' : 'd-none')" style="display: none">
    @*Show standard menu only if user is not guest user nor electronically signed guest user*@
    <div class="nav hidden" role="navigation" aria-label="Sidemenu Navigation" data-bind="css: { hidden : isGuestUserLoggedIn || isElectronicallyGuestLoggedIn || shouldHideOptions }">
        <ul class="nav cpq-scroll">
            <li class="side-menu-main-icon clearfix full-width" data-bind="css: { active: document.location.pathname == '/quotation/LoadQuote.aspx' || document.location.pathname == '/QuoteList' }">
                <a id="sideMenuLoadQuoteLink" href="/quotation/LoadQuote.aspx" class="sap-icon full-width" aria-label="@Translation.Get("quotation.ProposalMain.Existing")" data-bind="click: clearSessionStorageOnButtonClick, tooltip: { title: '@Translation.Get("quotation.ProposalMain.Existing")', placement: 'right', viewport: '#sideMenuContainer' }">
                    &#xe0c0;
                    <span class="left slide-out-section side-menu-expanded-section">
                        <span class="expanded-sidemenu-item" data-bind="visible: isSideMenuExpanded()">@Translation.Get("quotation.ProposalMain.Existing")</span>
                    </span>
                </a>
            </li>
            <li class="side-menu-main-icon clearfix full-width bulk-validation-side-menu-main-icon hidden" data-bind="css: { active: document.location.pathname == '/quotation/BulkValidation.aspx', 'hidden' : !bulkValidationShow || useNewQuoteEngine }">
                <a href="/quotation/BulkValidation.aspx" class="sap-icon full-width" aria-label="@Translation.Get("BulkValidation.Title")" data-bind="tooltip: { title: '@Translation.Get("BulkValidation.Title")', placement: 'right', viewport: '#sideMenuContainer' }">
                    &#xe059;
                    <span class="left slide-out-section side-menu-expanded-section">
                        <span class="expanded-sidemenu-item bulk-validation-list-header" data-bind="visible: isSideMenuExpanded()">@Translation.Get("BulkValidation.Title")</span>
                    </span>
                </a>
            </li>
            @IF (AssetsController.HasVisibleColumns())
            {
                <!-- ko if: !useNewQuoteEngine -->
                    <li class="side-menu-main-icon clearfix full-width" data-bind="css: { active: document.location.pathname == '/Assets' }">
                        <a href="/Assets" class="sap-icon full-width" aria-label="@Translation.Get("quotation.Assets")" data-bind="tooltip: { title: '@Translation.Get("quotation.Assets")', placement: 'right', viewport: '#sideMenuContainer' }">
                            &#xe124;
                            <span class="left slide-out-section side-menu-expanded-section">
                                <span class="expanded-sidemenu-item" data-bind="visible: isSideMenuExpanded()">@Translation.Get("quotation.Assets")</span>
                            </span>
                        </a>
                    </li>
                <!-- /ko -->
            }
            <li class="side-menu-main-icon clearfix full-width @(@ViewBag.CurrentPage == "Catalog" ? "active" : "")">
                <a id="CatalogId" href="/Catalogue/CategoryTree.aspx" class="sap-icon full-width" aria-label="@Translation.Get("View.ResponsiveLayout.Catalog")" data-bind="tooltip: { title: '@Translation.Get("View.ResponsiveLayout.Catalog")', placement: 'right', viewport: '#sideMenuContainer' }">
                    &#xe011;
                    <span class="left slide-out-section side-menu-expanded-section">
                        <span class="expanded-sidemenu-item" data-bind="visible: isSideMenuExpanded()">@Translation.Get("View.ResponsiveLayout.Catalog")</span>
                    </span>
                </a>
            </li>
            @IF (@ApprovalsController.GetParsedCPQTagValue(ApproveQuotesVisibilityCondition))
            {
                <li class="side-menu-main-icon clearfix full-width @(@ViewBag.CurrentPage == "Approvals" ? "active" : "")">
                    <a id="sideMenuLoadQuoteForApprovalsLink" href="/quotation/LoadQuote.aspx?tab=2" class="sap-icon full-width" aria-label="@Translation.Get("View.ResponsiveLayout.Approvals")" data-bind="click: clearSessionStorageOnButtonClick, tooltip: { title: '@Translation.Get("View.ResponsiveLayout.Approvals")', placement: 'right', viewport: '#sideMenuContainer' }">
                        &#xe04f;
                        <span class="left slide-out-section side-menu-expanded-section">
                            <span class="expanded-sidemenu-item" data-bind="visible: isSideMenuExpanded()">@Translation.Get("View.ResponsiveLayout.Approvals")</span>
                        </span>
                    </a>
                </li>
            }
            
            <li id="setupMenu" class="side-menu-main-icon hidden" data-bind="css: { hidden : !tryIsAdmin }, visible: setupLinksVisibility.isSetupLinkVisible() || setupLinksVisibility.isDevelopConsoleLinkVisible() || setupLinksVisibility.isScriptWorkbenchLinkVisible() || setupLinksVisibility.isEventLogLinkVisible()" style="display: none;">
                <button id="setupMenuDropdownTrigger" class="dropdown-toggle sap-icon" data-toggle="dropdown" aria-label="@Translation.Get("Menu.Setup")" data-bind="tooltip: { title: '@Translation.Get("Menu.Setup")', placement: 'right', viewport: '#sideMenuContainer' }, attr: { tabindex: isSideMenuExpanded() ? '-1' : '0'}" aria-expanded="false">&#xe00c;</button>
                <ul id="setupMenuDropdownContainer" class="left slide-out-section dropdown-menu slide-in-from-left side-menu-expanded-section" role="menu">
                    <li class="list-header dropdown-menu-list-header">
                        @Translation.Get("Menu.Setup")
                        <span tabindex="0" aria-label="@Translation.GetJS("MainMenu.ToggleSetup")" data-bind="click: toggleSetupMenuItems, executeOnEnter: toggleSetupMenuItems, visible: isSideMenuExpanded(), tooltip: { title: '@Translation.GetJS("MainMenu.ToggleSetup")', placement: 'auto top' }" class="sap-icon setup-menu-items-toggle">&#xe1e1;</span>
                    </li>
                    <li data-bind="if: setupLinksVisibility.isSetupLinkVisible(), css: { 'rolled-up-menu-item': areSetupMenuItemsRolledUp() }" role="menuitem">
                        <a href="/admin/AdminTransfer.aspx?Log=1" data-bind="click: clearSessionStorageOnButtonClick">
                            <span class="invisible-when-menu-expanded">&#xe00c;</span>&nbsp;@Translation.Get("Menu.Setup")
                        </a>
                    </li>
                    <li data-bind="if: setupLinksVisibility.isDevelopConsoleLinkVisible(), css: { 'rolled-up-menu-item': areSetupMenuItemsRolledUp() }" role="menuitem">
                        <a href="/DeveloperConsole" target="_blank">
                            <span class="invisible-when-menu-expanded">&#xe22c;</span>&nbsp;@Translation.Get("Menu.DeveloperConsole")
                        </a>
                    </li>
                    <li data-bind="if: setupLinksVisibility.isScriptWorkbenchLinkVisible(), css: { 'rolled-up-menu-item': areSetupMenuItemsRolledUp() }" role="menuitem">
                        <a id="ScriptWorkbenchId" data-bind="attr: { href: scriptWorkbenchHref }" target="_blank" rel="noopener noreferrer">
                            <span class="invisible-when-menu-expanded">&#xe22d;</span>&nbsp;@Translation.Get("Menu.ScriptWorkbench")
                        </a>
                    </li>
                    <li data-bind="if: setupLinksVisibility.isEventLogLinkVisible(), css: { 'rolled-up-menu-item': areSetupMenuItemsRolledUp() }" role="menuitem">
                        <a href="/SetupSpa/developer-tools/event-log" target="_blank">
                            <span class="invisible-when-menu-expanded">&#xe1a9;</span>&nbsp;@Translation.Get("xmlEventLog")
                        </a>
                    </li>
                </ul>
            </li>
            <li id="setupMenu" class="side-menu-main-icon hidden" data-bind="css: { hidden : !tryIsImpersonated || tryIsAdmin }">
                <a id="setupMenuDropdownTrigger" class="dropdown-toggle sap-icon" data-toggle="dropdown" href="#" aria-label="@Translation.Get("Menu.Setup")" title="@Translation.Get("Menu.Setup")">&#xe00c;</a>
                <ul id="setupMenuDropdownContainer" class="left slide-out-section dropdown-menu slide-in-from-left side-menu-expanded-section" role="menu">
                    <li class="list-header dropdown-menu-list-header">
                        @Translation.Get("Menu.Setup")
                        <span data-bind="click: toggleSetupMenuItems, visible: isSideMenuExpanded()" class="sap-icon setup-menu-items-toggle">&#xe1e1;</span>
                    </li>
                    <li data-bind="if: setupLinksVisibility.isDevelopConsoleLinkVisible(), css: { 'rolled-up-menu-item': areSetupMenuItemsRolledUp() }" role="menuitem">
                        <a href="/DeveloperConsole" target="_blank">
                            <span class="invisible-when-menu-expanded">&#xe22c;</span>&nbsp;@Translation.Get("Menu.DeveloperConsole")
                        </a>
                    </li>
                </ul>
            </li>
        </ul>
    </div>
    <a href="#" id="sidemenuToggle" class="side-menu-toggle rotated-side-menu-toggle" aria-label="@Translation.GetJS("MainMenu.ToggleMenu")" data-bind="click: toggleSideMenu, tooltip: { title: '@Translation.GetJS("MainMenu.ToggleMenu")', placement: 'auto left' }" aria-expanded="false">&#xe1c5;</a>
</div>
<script>
    $('.side-menu-toggle').click(function () {
        $("#wrap").toggleClass("side-menu-expanded");
        $('.side-menu-toggle').toggleClass('rotated-side-menu-toggle');

        // trigger tabdrop update on expanding/collapsing menu on quote pages
        if (document.location.pathname.indexOf('/quotation/') !== -1) {
            // timeout because of sidemenu animation
            setTimeout(function () {
                $(window).resize();
            }, 550)
        }
    });

    $('.list-header .sap-icon').click(function (event) {
        event.target.classList.toggle('rotated');
    });

    $('.list-header .sap-icon').on('keydown', function (event) {
        if (event.keyCode === 13) {
            event.target.classList.toggle('rotated');
        };
    });

    function encodeQueryStringParameter (key, value) {
        return encodeURIComponent(key) + '=' + encodeURIComponent(value);
    }

    $('#CatalogId').on('click', function (event) {
        var quoteId = sessionStorage.getItem("quoteId");

        if (quoteId && !isNaN(quoteId)) {
            var currentHref = $(this).attr('href');

            if (currentHref.endsWith("Catalogue/CategoryTree.aspx") != -1) {
                if (!currentHref.includes("quoteId")) {
                    var newHref = currentHref + "?" + encodeQueryStringParameter('quoteId', quoteId);
                    $(this).attr('href', newHref);
                }
            }
        }
    });
</script>

Pro Tips

  • Keep it professional: Avoid overly flashy designs that may distract users.
  • Maintain accessibility: Ensure color contrast meets accessibility standards.
  • Schedule a revert: Plan to switch back to your standard theme after the holidays.

Spread the Joy!

A Christmas-themed SAP CPQ tenant is a simple yet effective way to celebrate the season with your team and customers. So, go ahead—add some sparkle to your CPQ experience and make this holiday season unforgettable!