Forráskód Böngészése

[slim-sidebar] Styling changes

Storm Heg 3 éve
szülő
commit
877297f67e

+ 5 - 0
client/scss/settings/_variables.scss

@@ -100,8 +100,13 @@ $menu-width: 200px;
 $menu-width-slim: 60px;
 
 $menu-width-max: 320px;
+
 $mobile-nav-indent: 50px;
 
+// transitions
+// Please keep in sync with SIDEBAR_TRANSITION_DURATION variable in `client/src/components/Sidebar/Sidebar.tsx`
+$menu-transition-duration: 150ms;
+
 $focus-outline-width: 3px;
 
 $nav-wrapper-inner-z-index: 26;

+ 3 - 3
client/src/components/Sidebar/Sidebar.scss

@@ -7,9 +7,9 @@
     flex-direction: column;
     height: 100%;
     background: $nav-grey-3;
-    z-index: 1;
+    z-index: 3;
 
-    @include transition(width 0.3s ease);
+    @include transition(width $menu-transition-duration ease);
 
     &--slim {
         width: $menu-width-slim;
@@ -24,7 +24,7 @@
     }
 
     &__collapse-toggle {
-        @include transition(background-color 150ms ease);
+        @include transition(background-color $menu-transition-duration ease);
         position: absolute;
         top: 12px;
         left: 12px;

+ 5 - 2
client/src/components/Sidebar/Sidebar.tsx

@@ -4,6 +4,9 @@ import * as React from 'react';
 
 import Icon from '../Icon/Icon';
 
+// Please keep in sync with $menu-transition-duration variable in `client/scss/settings/_variables.scss`
+export const SIDEBAR_TRANSITION_DURATION = 150;
+
 export interface Strings {
   DASHBOARD: string;
   EDIT_YOUR_ACCOUNT: string,
@@ -62,7 +65,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = (
     setExpandingOrCollapsing(true);
     const finishTimeout = setTimeout(() => {
       setExpandingOrCollapsing(false);
-    }, 300);
+    }, SIDEBAR_TRANSITION_DURATION);
 
     return () => {
       clearTimeout(finishTimeout);
@@ -101,7 +104,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = (
     clearTimeout(stopPeekingTimeout.current);
     stopPeekingTimeout.current = setTimeout(() => {
       setPeeking(false);
-    }, 300);
+    }, SIDEBAR_TRANSITION_DURATION);
   };
 
   // Render modules

+ 1 - 4
client/src/components/Sidebar/SidebarPanel.scss

@@ -13,7 +13,7 @@
     flex-direction: column;
     overflow: hidden;
 
-    @include transition(left 0.3s ease);
+    @include transition(left $menu-transition-duration ease);
 
     &--visible {
         visibility: visible;
@@ -31,9 +31,6 @@
     &--open {
         left: $menu-width;
 
-        @at-root .sidebar--slim #{&} {
-            left: $menu-width-slim;
-        }
         // Don't apply this to nested submenus though
         @at-root .sidebar--slim .sidebar-panel #{&} {
             left: $menu-width;

+ 38 - 2
client/src/components/Sidebar/menu/LinkMenuItem.tsx

@@ -34,11 +34,47 @@ export const LinkMenuItem: React.FunctionComponent<MenuItemProps<LinkMenuItemDef
     + (isInSubMenu ? ' sidebar-menu-item--in-sub-menu' : '')
   );
 
+  const [peeking, setPeeking] = React.useState(false);
+  const wrapperRef = React.useRef<HTMLLIElement | null>(null);
+  React.useEffect(() => {
+    if (!wrapperRef.current) {
+      return;
+    }
+
+    const element = wrapperRef.current;
+    let startPeekingTimeout;
+    let stopPeekingTimeout;
+
+    const onMouseEnterHandler = () => {
+      clearTimeout(startPeekingTimeout);
+      clearTimeout(stopPeekingTimeout);
+      startPeekingTimeout = setTimeout(() => {
+        setPeeking(true);
+      }, 250);
+    };
+
+    const onMouseLeaveHandler = () => {
+      clearTimeout(startPeekingTimeout);
+      clearTimeout(stopPeekingTimeout);
+      stopPeekingTimeout = setTimeout(() => {
+        setPeeking(false);
+      }, 250);
+    };
+
+    element.addEventListener('mouseenter', onMouseEnterHandler);
+    element.addEventListener('mouseleave', onMouseLeaveHandler);
+  }, []);
+
   return (
-    <li className={className}>
-      <a href="#" onClick={onClick} className={item.classNames}>
+    <li className={className} ref={wrapperRef}>
+      <a href="#" onClick={onClick} className={`sidebar-menu-item__link ${item.classNames}`}>
         {item.iconName && <Icon name={item.iconName} className="icon--menuitem" />}
         <span className="menuitem-label">{item.label}</span>
+        <div className={'menuitem-tooltip' + (peeking ? ' menuitem-tooltip--visible' : '')}>
+          <div className="menuitem-tooltip__inner">
+            {item.label}
+          </div>
+        </div>
       </a>
     </li>
   );

+ 73 - 9
client/src/components/Sidebar/menu/MenuItem.scss

@@ -1,16 +1,21 @@
 .sidebar-menu-item {
-    > a {
+    $root: &;
+    @include transition(border-color $menu-transition-duration ease);
+    position: relative;
+
+    &__link {
+        @include transition(border-color $menu-transition-duration ease, background-color $menu-transition-duration ease);
         position: relative;
+        display: flex;
         white-space: nowrap;
         border-left: 3px solid transparent;
 
         -webkit-font-smoothing: auto;
         text-decoration: none;
-        display: block;
         color: $color-menu-text;
-        padding: 0.8em 1.7em;
-        font-size: 1em;
-        font-weight: normal;
+        padding: 11px 20px;
+        font-size: 13px;
+        font-weight: 400;
 
         // Note, font-weights lower than normal,
         // and font-size smaller than 1em (80% ~= 12.8px),
@@ -23,8 +28,6 @@
             text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
         }
 
-        @include transition(border-color 0.3s ease);
-
         &:before {
             font-size: 1rem;
             vertical-align: -15%;
@@ -43,8 +46,10 @@
     }
 
     &--in-sub-menu {
-        white-space: normal;
-        padding: 0.9em 1.7em 0.9em 4.5em;
+        #{$root}__link {
+            // Links inside a submenu should have normal wrapping
+            white-space: normal;
+        }
 
         &:hover {
             background-color: rgba(100, 100, 100, 0.2);
@@ -61,3 +66,62 @@
         }
     }
 }
+
+.menuitem-label {
+    @include transition(opacity $menu-transition-duration ease);
+}
+
+.menuitem-tooltip {
+    position: absolute;
+    left: calc(100% + 5px);
+    z-index: 5;
+    top: 0;
+    bottom: 0;
+    margin: auto 0;
+    height: 27px;
+    pointer-events: none;
+    opacity: 0;
+    transform: translate3d(-20px, 0, 0);
+    transition: opacity $menu-transition-duration cubic-bezier(0.42, 0, 0.58, 1), transform $menu-transition-duration cubic-bezier(0.42, 0, 0.58, 1);
+
+    &__inner {
+        width: 100%;
+        box-sizing: border-box;
+        border-radius: 3px;
+        position: relative;
+        padding: 7px 9px;
+        color: #ccc;
+        height: 27px;
+        font-size: 12px;
+        line-height: 11px;
+        background: #262626;
+
+        &::before {
+            content: '';
+            position: absolute;
+            display: block;
+            width: 0;
+            left: 0;
+            top: 50%;
+            height: 0;
+            border-style: solid;
+            border-width: 5px 5px 5px 0;
+            border-color: transparent #262626 transparent transparent;
+            transform: translate(calc(-100%), -50%);
+        }
+    }
+
+    &--visible {
+        .sidebar-collapsed & {
+            pointer-events: initial;
+            opacity: 1;
+            transform: translate3d(0, 0, 0);
+        }
+    }
+}
+
+.sidebar--slim {
+    .menuitem-label {
+        opacity: 0;
+    }
+}

+ 3 - 2
client/src/components/Sidebar/menu/PageExplorerMenuItem.tsx

@@ -10,6 +10,7 @@ import { Provider } from 'react-redux';
 import PageExplorer, { initPageExplorerStore } from '../../PageExplorer';
 import { openPageExplorer, closePageExplorer } from '../../PageExplorer/actions';
 import { SidebarPanel } from '../SidebarPanel';
+import { SIDEBAR_TRANSITION_DURATION } from '../Sidebar';
 
 export const PageExplorerMenuItem: React.FunctionComponent<MenuItemProps<PageExplorerMenuItemDefinition>> = (
   { path, item, state, dispatch, navigate }) => {
@@ -40,7 +41,7 @@ export const PageExplorerMenuItem: React.FunctionComponent<MenuItemProps<PageExp
         if (store.current) {
           store.current.dispatch(closePageExplorer());
         }
-      }, 300);
+      }, SIDEBAR_TRANSITION_DURATION);
     }
   }, [isOpen]);
 
@@ -74,7 +75,7 @@ export const PageExplorerMenuItem: React.FunctionComponent<MenuItemProps<PageExp
 
   return (
     <li className={className}>
-      <Button dialogTrigger={true} onClick={onClick}>
+      <Button dialogTrigger={true} onClick={onClick} className="sidebar-menu-item__link">
         <Icon name="folder-open-inverse" className="icon--menuitem" />
         <span className="menuitem-label">{item.label}</span>
         <Icon className={sidebarTriggerIconClassName} name="arrow-right" />

+ 47 - 19
client/src/components/Sidebar/menu/SubMenuItem.scss

@@ -1,20 +1,31 @@
 .sidebar-sub-menu-trigger-icon {
     display: block;
-    width: 1.5em;
-    height: 1.5em;
+    width: 20px;
+    height: 20px;
     position: absolute;
-    top: 0.8125em;
-    right: 0.5em;
+    right: 15px;
+    top: 0;
+    bottom: 0;
+    margin: auto 0;
 
-    @include transition(transform 0.3s ease, top 0.3s ease, right 0.3s ease, width 0.3s ease, height 0.3s ease);
+    @include transition(transform $menu-transition-duration ease, width $menu-transition-duration ease, height $menu-transition-duration ease);
 
     &--open {
         transform-origin: 50% 50%;
         transform: rotate(180deg);
     }
+
+    .sidebar--slim & {
+        width: 16px;
+        height: 16px;
+        transform: translate3d(13px, 0, 0);
+    }
 }
 
+
 .sidebar-sub-menu-panel {
+    display: flex;
+    flex-direction: column;
     background: $nav-submenu-bg;
     width: $menu-width;
     height: 100vh;
@@ -43,22 +54,17 @@
         }
     }
 
-    > ul,
-    ul > li {
-        margin: 0;
-        padding: 0;
-        list-style-type: none;
-    }
 
     ul > li {
         position: relative;
 
-        @include transition(border-color 0.3s ease);
+        @include transition(border-color $menu-transition-duration ease);
     }
 
     > ul {
-        overflow: auto;
         flex-grow: 1;
+        padding: 0;
+        margin: 0;
     }
 
     > ul > li {
@@ -71,18 +77,40 @@
         text-align: center;
         color: $color-menu-text;
     }
+
+    &--visible {
+        visibility: visible;
+        box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
+    }
+
+    @at-root .sidebar--slim #{&} {
+        transform: translate3d($menu-width-slim - $menu-width, 0, 0);
+    }
+    // Don't apply this to nested submenus though
+    @at-root .sidebar--slim .sidebar-sub-menu-panel #{&} {
+        transform: translate3d(0, 0, 0);
+    }
+
+    &--open {
+        transform: translate3d($menu-width, 0, 0);
+
+        // If another submenu is opening, display this menu behind it
+        z-index: -1;
+
+        @at-root .sidebar--slim #{&} {
+            transform: translate3d($menu-width-slim, 0, 0);
+        }
+        // Don't apply this to nested submenus though
+        @at-root .sidebar--slim .sidebar-sub-menu-panel #{&} {
+            transform: translate3d($menu-width, 0, 0);
+        }
+    }
 }
 
 .sidebar-sub-menu-item {
     &--open {
-        background: $nav-submenu-bg;
-
         > a {
             text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
-
-            &:hover {
-                background-color: transparent;
-            }
         }
     }
 }

+ 4 - 3
client/src/components/Sidebar/menu/SubMenuItem.tsx

@@ -6,6 +6,7 @@ import Icon from '../../Icon/Icon';
 
 import { renderMenu } from '../modules/MainMenu';
 import { SidebarPanel } from '../SidebarPanel';
+import { SIDEBAR_TRANSITION_DURATION } from '../Sidebar';
 import { MenuItemDefinition, MenuItemProps } from './MenuItem';
 
 interface SubMenuItemProps extends MenuItemProps<SubMenuItemDefinition> {
@@ -28,7 +29,7 @@ export const SubMenuItem: React.FunctionComponent<SubMenuItemProps> = (
       // to finish before making it invisible
       setTimeout(() => {
         setIsVisible(false);
-      }, 300);
+      }, SIDEBAR_TRANSITION_DURATION);
     }
   }, [isOpen]);
 
@@ -67,7 +68,7 @@ export const SubMenuItem: React.FunctionComponent<SubMenuItemProps> = (
       <a
         href="#"
         onClick={onClick}
-        className={item.classNames}
+        className={`sidebar-menu-item__link ${item.classNames}`}
         aria-haspopup="true"
         aria-expanded={isOpen ? 'true' : 'false'}
       >
@@ -84,7 +85,7 @@ export const SubMenuItem: React.FunctionComponent<SubMenuItemProps> = (
           <ul aria-labelledby={`wagtail-sidebar-submenu${path.split('.').join('-')}-title`}>
             {renderMenu(path, item.menuItems, slim, state, dispatch, navigate)}
           </ul>
-          {item.footerText && <p className="sidebar-panel__footer">{item.footerText}</p>}
+          {item.footerText && <p className="sidebar-sub-menu-panel__footer">{item.footerText}</p>}
         </div>
       </SidebarPanel>
     </li>

+ 1 - 1
client/src/components/Sidebar/modules/CustomBranding.scss

@@ -7,7 +7,7 @@
     margin: 2em auto;
     text-align: center;
     padding: 10px 0;
-    transition: padding 0.3s ease;
+    transition: padding $menu-transition-duration ease;
 
     &:hover {
         color: $color-white;

+ 16 - 14
client/src/components/Sidebar/modules/MainMenu.scss

@@ -3,7 +3,7 @@
     overflow: auto;
     overflow-x: hidden;
     margin-bottom: 50px;
-    @include transition(margin-bottom 0.3s ease);
+    @include transition(margin-bottom $menu-transition-duration ease);
 
     &--open-footer {
         margin-bottom: 127px;
@@ -19,7 +19,7 @@
     ul > li {
         position: relative;
 
-        @include transition(border-color 0.3s ease);
+        @include transition(border-color $menu-transition-duration ease);
     }
 
     *:focus {
@@ -29,6 +29,7 @@
     .icon--menuitem {
         width: 1.25em;
         height: 1.25em;
+        min-width: 1.25em;
         margin-right: 0.5em;
         vertical-align: text-top;
     }
@@ -43,10 +44,10 @@
 
     > ul > li > a {
         // Need !important to override body.ready class
-        transition: padding 0.3s ease !important;
+        transition: padding $menu-transition-duration ease !important;
 
         .menuitem-label {
-            transition: opacity 0.3s ease;
+            transition: opacity $menu-transition-duration ease;
         }
     }
 
@@ -73,7 +74,7 @@
     width: $menu-width;
     bottom: 0;
     background-color: $nav-footer-submenu-bg;
-    transition: width 0.3s ease !important;  // Override body.ready
+    transition: width $menu-transition-duration ease !important;  // Override body.ready
 
     > ul,
     ul > li {
@@ -85,11 +86,11 @@
     ul > li {
         position: relative;
 
-        @include transition(border-color 0.3s ease);
+        @include transition(border-color $menu-transition-duration ease);
     }
 
     > ul {
-        @include transition(max-height 0.3s ease);
+        @include transition(max-height $menu-transition-duration ease);
 
         max-height: 0;
 
@@ -132,6 +133,13 @@
             }
         }
 
+        &--icon {
+            height: 1.5em;
+            width: 1.5em;
+            position: absolute;
+            right: 0.25em;
+        }
+
         em {
             box-sizing: border-box;
             padding-right: 1.8em;
@@ -145,13 +153,7 @@
             text-overflow: ellipsis;
             position: absolute;
             left: 50px;  // Width of avatar
-            transition: left 0.3s ease;
-
-            &:after {
-                font-size: 1.5em;
-                position: absolute;
-                right: 0.25em;
-            }
+            transition: left $menu-transition-duration ease;
         }
     }
 

+ 20 - 3
client/src/components/Sidebar/modules/MainMenu.tsx

@@ -1,6 +1,7 @@
 /* eslint-disable react/prop-types */
 
 import * as React from 'react';
+import Icon from '../../Icon/Icon';
 
 import { LinkMenuItemDefinition } from '../menu/LinkMenuItem';
 import { MenuItemDefinition } from '../menu/MenuItem';
@@ -62,13 +63,14 @@ interface MenuProps {
   accountMenuItems: MenuItemDefinition[];
   user: MainMenuModuleDefinition['user'];
   slim: boolean;
+  expandingOrCollapsing: boolean;
   currentPath: string;
   strings: Strings;
   navigate(url: string): Promise<void>;
 }
 
 export const Menu: React.FunctionComponent<MenuProps> = (
-  { menuItems, accountMenuItems, user, slim, currentPath, strings, navigate }) => {
+  { menuItems, accountMenuItems, user, expandingOrCollapsing, slim, currentPath, strings, navigate }) => {
   // navigationPath and activePath are two dot-delimited path's referencing a menu item
   // They are created by concatenating the name fields of all the menu/sub-menu items leading to the relevant one.
   // For example, the "Users" item in the "Settings" sub-menu would have the path 'settings.users'
@@ -134,6 +136,16 @@ export const Menu: React.FunctionComponent<MenuProps> = (
     };
   }, []);
 
+  // Whenever the parent Sidebar component collapses or expands, close any open menus
+  React.useEffect(() => {
+    if (expandingOrCollapsing) {
+      dispatch({
+        type: 'set-navigation-path',
+        path: ''
+      });
+    }
+  }, [expandingOrCollapsing]);
+
   const onClickAccountSettings = (e: React.MouseEvent) => {
     e.preventDefault();
 
@@ -169,8 +181,12 @@ export const Menu: React.FunctionComponent<MenuProps> = (
             <span className="avatar square avatar-on-dark">
               <img src={user.avatarUrl} alt="" />
             </span>
-            <em className={'icon ' + (accountSettingsOpen ? 'icon-arrow-down-after' : 'icon-arrow-up-after')}>
+            <em>
               {user.name}
+              <Icon
+                className="sidebar-footer__account--icon"
+                name={(accountSettingsOpen ? 'arrow-down' : 'arrow-up')}
+              />
             </em>
           </div>
 
@@ -201,13 +217,14 @@ export class MainMenuModuleDefinition implements ModuleDefinition {
     this.user = user;
   }
 
-  render({ slim, key, currentPath, strings, navigate }) {
+  render({ slim, expandingOrCollapsing, key, currentPath, strings, navigate }) {
     return (
       <Menu
         menuItems={this.menuItems}
         accountMenuItems={this.accountMenuItems}
         user={this.user}
         slim={slim}
+        expandingOrCollapsing={expandingOrCollapsing}
         key={key}
         currentPath={currentPath}
         strings={strings}

+ 53 - 54
client/src/components/Sidebar/modules/Search.scss

@@ -1,46 +1,36 @@
 // stylelint-disable declaration-no-important
 .sidebar-search {
+    $root: &;
     position: relative;
-    padding: 0 1em 1em;
-    margin: 0;
-    width: 100%;
     box-sizing: border-box;
+    padding: 0 20px;
+    display: flex;
+    align-items: center;
+    flex-direction: row-reverse;
 
-    label {
-        @include visuallyhidden;
+    .sidebar--slim & {
+        padding: 0;
     }
 
-    input,
-    button {
-        border-radius: 0;
-        font-size: 1em;
-        border: 0;
+    &__label {
+        @include visuallyhidden;
     }
 
-    input {
-        cursor: pointer;
-        border: 1px solid $nav-search-border;
-        background-color: $nav-search-bg;
-        color: $nav-search-color;
-        padding: 0.8em 2.5em 0.8em 1em;
-        font-weight: 600;
-        opacity: 1;
+    // Beat specificity
+    input:not([type='submit']) {
         // Need !important to override body.ready class
-        transition: background-color 0.2s ease, opacity 0.3s ease !important;
+        transition: background-color 0.2s ease, opacity 600ms ease-in-out !important;
         visibility: hidden;
+        font-size: 13px;
+        font-weight: 400;
+        background-color: transparent;
+        border: 0;
+        padding: 12px 0;
+        color: $color-menu-text;
+        -webkit-font-smoothing: auto;
 
-        @at-root .sidebar--slim #{&} {
-            opacity: 0;
-        }
-
-        &:hover {
-            background-color: $nav-search-hover-bg;
-        }
-
-        &:active,
-        &:focus {
-            background-color: $nav-search-focus-bg;
-            color: $nav-search-focus-color;
+        .sidebar--slim & {
+            display: none;
         }
 
         &::placeholder {
@@ -48,30 +38,18 @@
         }
     }
 
-    &--visible input {
-        visibility: visible;
-    }
-
-    button {
+    &__submit {
         background-color: transparent;
-        position: absolute;
-        top: 0;
-        right: 1em;
-        bottom: 0;
+        border: 0;
+        color: #ccc;
         padding: 0;
-        width: 3em;
-        transition: right 0.3s ease;
+        width: 35px;
+        height: 35px;
+        font-size: 17px;
+        transition: opacity 600ms ease-in-out;
 
-        @at-root .sidebar--slim #{&} {
-            right: 0.5em;
-        }
-
-        &:hover {
-            background-color: $nav-item-hover-bg;
-        }
-
-        &:active {
-            background-color: $nav-item-active-bg;
+        .sidebar--slim & {
+            width: 100%;
         }
 
         &:before {
@@ -81,8 +59,29 @@
             content: map-get($icons, 'search');
             display: block;
             height: 100%;
-            line-height: 3.3em;
-            padding: 0 1em;
+            line-height: 35px;
+            text-align: left;
+
+            .sidebar--slim & {
+                text-align: center;
+            }
+        }
+
+        &:hover {
+            background-color: transparent;
+        }
+    }
+
+    &--visible {
+        input:not([type='submit']) {
+            visibility: visible;
         }
     }
 }
+
+.sidebar-search--slim.sidebar-search--visible {
+    .sidebar-search__submit,
+    input:not([type=submit]) {
+        opacity: 0;
+    }
+}

+ 3 - 5
client/src/components/Sidebar/modules/Search.tsx

@@ -37,11 +37,9 @@ export const SearchInput: React.FunctionComponent<SearchInputProps> = (
 
   return (
     <form className={className} action={searchUrl} method="get" onSubmit={onSubmitForm}>
-      <div>
-        <label htmlFor="menu-search-q">{strings.SEARCH}</label>
-        <input type="text" id="menu-search-q" name="q" placeholder={strings.SEARCH} />
-        <button className="button" type="submit">{strings.SEARCH}</button>
-      </div>
+      <label className="sidebar-search__label" htmlFor="menu-search-q">{strings.SEARCH}</label>
+      <input className="sidebar-search__input" type="text" id="menu-search-q" name="q" placeholder={strings.SEARCH} />
+      <button className="button sidebar-search__submit" type="submit">{strings.SEARCH}</button>
     </form>
   );
 };

+ 69 - 69
client/src/components/Sidebar/modules/WagtailBranding.scss

@@ -1,94 +1,59 @@
 // stylelint-disable declaration-no-important
+
+// Wagging animation
+@keyframes tail-wag {
+    from {
+        transform: rotate(-3deg);
+    }
+
+    to {
+        transform: rotate(7deg);
+    }
+}
+
 .sidebar-wagtail-branding {
+    $root: &;
+    position: relative;
     display: block;
     align-items: center;
     color: #aaa;
     -webkit-font-smoothing: auto;
-    position: relative;
-    margin: 2em auto;
+    margin: 4.5em auto 2.5em;
     text-align: center;
-    padding: 10px 0;
-    transition: padding 0.3s ease;
+    width: 100px;
+    height: 100px;
+    transition: transform 150ms cubic-bezier(0.28, 0.15, 0, 2.1), width 150ms ease, height 150ms ease;
 
     &:hover {
         color: $color-white;
+        transform: rotate(4deg);
     }
 
-    @at-root .sidebar--slim #{&} {
-        padding: 40px 0;
+    // Reduce overall size when in slim mode
+    .sidebar--slim & {
+        width: 40px;
+        height: 40px;
     }
 
-    &__desktop {
-        @keyframes tail-wag {
-            from {
-                transform: rotate(-3deg);
-            }
-
-            to {
-                transform: rotate(7deg);
-            }
-        }
-
-        position: relative;
-        width: 100px;
-        height: 100px;
-        background-color: #555;
-        border-radius: 50%;
-        margin: 0 auto;
-        transition: transform 0.3s cubic-bezier(0.28, 0.15, 0, 2.1), width 0.3s ease, height 0.3s ease;
-
-        > div {
-            margin: auto;
-            position: relative;
-            width: 52px;
-            height: 100px;
-            transition: width 0.3s ease, height 0.3s ease;
-
-            @at-root .sidebar--slim #{&} {
-                width: 20px;
-                height: 40px;
-            }
-
-            @at-root .page404__bg #{&} {
-                width: auto;
-                height: auto;
-                position: static;
-            }
-        }
-
-        img {
-            display: block;
-            left: 0;
-            top: 0;
-            width: 100%;
-            height: 100%;
-            position: absolute;
-            transition: inherit;
-
-            &[data-part='eye--open'] {
-                display: inline !important; // doesn't work without !important
-            }
-
-            &[data-part='eye--closed'] {
-                display: none !important;
-            }
-        }
+    // Remove background on 404 page
+    .page404__bg & {
+        background-color: transparent;
+    }
 
+    // Set wagging styles
+    &--wagging {
         &:hover {
-            transform: rotate(4deg);
-        }
-
-        &--wagging:hover {
             transform: rotate(8deg);
             transition: transform 1.2s ease;
 
-            img {
+            #{$root}__icon {
                 // stylelint-disable max-nesting-depth
                 &[data-part='tail'] {
                     animation: tail-wag 0.09s alternate;
                     animation-iteration-count: infinite;
                 }
 
+                // TODO: Fix legacy specificity issues
                 &[data-part='eye--open'] {
                     display: none !important;
                 }
@@ -98,14 +63,49 @@
                 }
             }
         }
+    }
 
-        @at-root .sidebar--slim #{&} {
+    // Bird wrapper
+    &__icon-wrapper {
+        margin: auto;
+        position: relative;
+        width: 100px;
+        height: 100px;
+        background-color: #3a3a3a;
+        border-radius: 50%;
+        transition: width 150ms ease, height 150ms ease;
+
+        .sidebar--slim & {
             width: 40px;
             height: 40px;
         }
 
-        @at-root .page404__bg #{&} {
-            background-color: transparent;
+        .page404__bg & {
+            width: auto;
+            height: auto;
+            position: static;
+        }
+    }
+
+    // Bird icons
+    &__icon {
+        display: block;
+        left: 0;
+        top: 0;
+        width: 70%;
+        height: 70%;
+        position: absolute;
+        margin: auto;
+        bottom: 0;
+        right: 0;
+
+        // TODO: Fix legacy specificity issues
+        &[data-part='eye--open'] {
+            display: inline !important;
+        }
+
+        &[data-part='eye--closed'] {
+            display: none !important;
         }
     }
 }

+ 14 - 10
client/src/components/Sidebar/modules/WagtailBranding.tsx

@@ -54,19 +54,23 @@ const WagtailBranding: React.FunctionComponent<WagtailBrandingProps> = ({ homeUr
   };
 
   const desktopClassName = (
-    'sidebar-wagtail-branding__desktop'
-    + (isWagging ? ' sidebar-wagtail-branding__desktop--wagging' : '')
+    'sidebar-wagtail-branding'
+    + (isWagging ? ' sidebar-wagtail-branding--wagging' : '')
   );
 
   return (
-    <a className="sidebar-wagtail-branding" href="#" onClick={onClick} aria-label={strings.DASHBOARD}>
-      <div className={desktopClassName} onMouseMove={onMouseMove} onMouseLeave={onMouseLeave}>
-        <div>
-          <img data-part="body" src={images.desktopLogoBody} alt="" />
-          <img data-part="tail" src={images.desktopLogoTail} alt="" />
-          <img data-part="eye--open" src={images.desktopLogoEyeOpen} alt="" />
-          <img data-part="eye--closed" src={images.desktopLogoEyeClosed} alt="" />
-        </div>
+    <a
+      className={desktopClassName} href="#" aria-label={strings.DASHBOARD}
+      onClick={onClick} onMouseMove={onMouseMove} onMouseLeave={onMouseLeave}
+    >
+      <div className="sidebar-wagtail-branding__icon-wrapper">
+        <img className="sidebar-wagtail-branding__icon" data-part="body" src={images.desktopLogoBody} alt="" />
+        <img className="sidebar-wagtail-branding__icon" data-part="tail" src={images.desktopLogoTail} alt="" />
+        <img className="sidebar-wagtail-branding__icon" data-part="eye--open" src={images.desktopLogoEyeOpen} alt="" />
+        <img
+          className="sidebar-wagtail-branding__icon" data-part="eye--closed" src={images.desktopLogoEyeClosed}
+          alt=""
+        />
       </div>
     </a>
   );