• Browser
  • Mail
  • News
  • Community
  • About
Register Login
HomeBlogsForumThemesContributeSocial

Vivaldi

  • Browser
  • Mail
  • News
  • Community
  • About

Navigation

    • Home
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    1. Home
    2. Community & Services
    3. Themes
    4. Guide | Creating Fully Featured Custom Icon Sets for Themes

    Guide | Creating Fully Featured Custom Icon Sets for Themes

    Themes
    1
    3
    758
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • nomadic
      N
      nomadic Soprano
      last edited by nomadic

      Note: I am currently working on writing all the posts, but figured it was better to post them as they are completed rather than all at once.


      While the number of people willing to go through the trouble of creating an icon set may be fairly low (especially since the contest is over), it is still good to have all the information about how to create a fully featured icon set available. The Create a custom icon set help page is a good place to start, but it lacks all the details necessary to take full advantage of what is available.

      Here are some guides on how to handle some of the more difficult icons and some tips for achieving interesting effects.


      (WIP = Work in Progress)

      Icons:

      • Analog Clock Dial - Status Bar Button - (WIP)
        • Dial hands
        • Countdown timer
        • Alarm
      • Downloads - Address / Panel Bar Button - (WIP)
        • Lining up the progress indicator
      • Page Tiling - Status Bar Button - (WIP)
        • Tiling horizontal/vertical state
      • Panel Toggle - Address Bar Button - (WIP)
        • Panel open/closed state
      • Toggle Images - Status Bar Button - (WIP)
        • Reflecting selected options

      General State Variables:

      • --activeButton CSS variable
        • Changing appearance of the active panel button icon
      • --buttonHover CSS variable - (WIP)
        • Changing appearance of icons when their button is hovered
      • Custom CSS variables
        • Using custom CSS to add additional state variables

      Misc:

      • Using theme colors - (WIP)
      • Using fallback values to properly show icons on the themes page - (WIP)
      • Using Vivaldi's predefined gradients and masks - (WIP)

      If you have any of your own tips and/or tricks, feel free to share them below and I will link them in this first post!

      1 Reply Last reply Reply Quote 7
      • nomadic
        N
        nomadic Soprano
        last edited by nomadic

        Custom CSS State Variables

        Throughout Vivaldi's various toolbars, there are several buttons with icons that are displayed differently depending on different states of the browser. The mail Read/Unread icons in the theme editor are 2 states of the same icon, each with their own entry in the editor, while the Image Toggle button has 1 single icon with numerous different states handled by CSS variables.

        Unfortunately, there are icons that lose functionality when you customize the icon. The Break Mode button, for example, shows a play or pause icon depending on whether break mode is active or not, when you use the default icon, but when it is customized, you lose the ability to display the play button state.

        While we wait for Vivaldi to hopefully add more icon states, either through individual states in the theme editor or CSS variable state control, we can accomplish some of the different state changes with a CSS modification. Icon sets dependent on this CSS mod can still be uploaded to the Theme Store, but people without the mod won't get the benefits of the additional states. Just make sure to have the default state of the icon be usable without the mod and link to this post in the theme description to help people unlock the additional functionality.

        • I tried to find as many different icons dependant on different states as possible, but I might have missed some. Please let me know if there are any icons/states that are missing.
        • Watch the change logs for VB-96517 - Themes and multi state buttons to see when some of these changes are hopefully implemented.

        The CSS Mod: (Instructions for installing a CSS mod are here.)

        /* Break mode state variable */
        #browser {
          --breakModeActive: 0;
        }
        #browser.break-mode {
          --breakModeActive: 1;
        }
        
        /* Hidden extension toggle state variable */
        #browser .toolbar-extensions {
          --extensionsExpanded: 0;
        }
        #browser .toolbar-extensions:has(.ExtensionIcon--Hidden),
        #browser:has(.extensionIconPopupMenu) .toolbar-extensions {
          --extensionsExpanded: 1;
        }
        
        /* Download state variables */
        /* TODO: Find way to detect completed download (for --downloadCompleted) without JS */
        .button-toolbar > button[name="DownloadButton"],
        .button-toolbar > button[name="PanelDownloads"] {
          --downloadInProgress: 0;
          --downloadCompleted: 0;
        }
        .button-toolbar > button[name="DownloadButton"]:has(+ .progress),
        .button-toolbar > button[name="PanelDownloads"]:has(+ .progress) {
          --downloadInProgress: 1;
        }
        
        /* Mail rendering method state variable */
        /* TODO: Make independent of English interface language setting */
        .button-toolbar > button[name="MailRenderingMethod"] {
          --mailRenderingIsHTML: 1;
        }
        .button-toolbar > button[name="MailRenderingMethod"][title*="html" i] {
          --mailRenderingIsHTML: 0;
        }
        
        /* Show Mail threads state variable */
        /* TODO: Make independent of English interface language setting */
        .button-toolbar > button[name="MailViewThreading"] {
          --mailThreadsAreShown: 0;
        }
        .button-toolbar > button[name="MailRenderingMethod"][title*="hide" i] {
          --mailThreadsAreShown: 1;
        }
        
        /* Mail view state variables */
        .button-toolbar > button[name="MailViewLayout"] {
          --mailViewIsHorizontal: 0;
          --mailViewIsVertical: 0;
          --mailViewIsVerticalWide: 0;
        }
        #browser:has(#mail-view.vertical) .button-toolbar > button[name="MailViewLayout"] {
          --mailViewIsVertical: 1;
        }
        #browser:has(#mail-view.vertical_wide) .button-toolbar > button[name="MailViewLayout"] {
          --mailViewIsVerticalWide: 1;
        }
        #browser:has(#mail-view.horizontal) .button-toolbar > button[name="MailViewLayout"] {
          --mailViewIsHorizontal: 1;
        }
        
        /* Mail Flag state variable */
        /* TODO: Not working and can't handle different colors */
        /* .button-toolbar > button[name="MailMsgFlag"] {
          --mailWillFlag: 1;
        }
        #browser:has(.vivaldi-tree .tree-row[data-selected] > .mail_entry > .mail_entry_row:not(> .flag-color)) .button-toolbar > button[name="MailMsgFlag"],
        #browser:has(.vivaldi-tree .tree-row[data-selected] > .mail_entry > label:not(.mailattachment):not(.labels):not(> span)) .button-toolbar > button[name="MailMsgFlag"] {
          --mailWillFlag: 0;
        } */
        
        /* Sync status state variable */
        /* TODO: Don't know what broken sync looks like, and connected/disconnected is dependant on language */
        
        /* Image toggle state variable for missing animation loop state */
        .button-toolbar > button[name="ImagesToggle"][style*="--displayAnimationOnce: none; --displayAnimationNever: none;"] {
          --displayAnimationLoop: block;
        }
        
        /* Image toggle state variables that are usable in calculations */
        .button-toolbar > button[name="ImagesToggle"] {
          --allImagesAreDisplayed: 0;
          --cachedImagesAreDisplayed: 0;
          --noImagesAreDisplayed: 0;
          --animationsArePlayedLooped: 0;
          --animationsArePlayedOnce: 0;
          --animationsArePlayedNever: 0;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayImagesAll: block;"] {
          --allImagesAreDisplayed: 1;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayImagesCached: block;"] {
          --cachedImagesAreDisplayed: 1;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayImagesNever: block;"] {
          --noImagesAreDisplayed: 1;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayAnimationOnce: none; --displayAnimationNever: none;"] {
          --animationsArePlayedLooped: 1;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayAnimationOnce: block;"] {
          --animationsArePlayedOnce: 1;
        }
        .button-toolbar > button[name="ImagesToggle"][style*="--displayAnimationNever: block;"] {
          --animationsArePlayedNever: 1;
        }
        
        /* Tab tiling toggle state variables that are usable in calculations */
        .button-toolbar > button[name="TilingToggle"] {
          --tileColumnIsDisplayed: 0;
          --tileRowIsDisplayed: 0;
        }
        .button-toolbar > button[name="TilingToggle"][style*="--displayTileColumn: unset;"] {
          --tileColumnIsDisplayed: 1;
        }
        .button-toolbar > button[name="TilingToggle"][style*="--displayTileRow: unset;"] {
          --tileRowIsDisplayed: 1;
        }
        
        /* Clock state variables that are usable in calculations */
        .button-toolbar > button[name="Clock"] {
          --clockCountdownSet: 0;
          --clockAlarmSet: 0;
        }
        .button-toolbar > button[name="Clock"][style*="--countdownHourPercent"],
        .button-toolbar > button[name="Clock"][style*="--countdownMinutePercent"],
        .button-toolbar > button[name="Clock"][style*="--countdownSecondPercent"] {
          --clockCountdownSet: 1;
        }
        .button-toolbar.ClockButton--alarm > button[name="Clock"] {
          --clockAlarmSet: 1;
        }
        
        /* Page action state variable */
        .button-toolbar > button[name="PageActions"] {
          --pageActionActive: 0;
        }
        .button-toolbar > button[name="PageActions"].button-on {
          --pageActionActive: 1;
        }
        
        /* Capture Images state variable */
        .button-toolbar > button[name="CaptureImages"] {
          --captureImagesActive: 0;
        }
        #browser:has(#capture-area) .button-toolbar > button[name="CaptureImages"] {
          --captureImagesActive: 1;
        }
        
        1 Reply Last reply Reply Quote 3
        • nomadic
          N
          nomadic Soprano
          last edited by nomadic

          --activeButton CSS Variable

          Vivaldi has a few general purpose CSS variables that can be used to manipulate the appearance of icons. One of these variables is the --activeButton variable, which is set on each panel toolbar button to indicate if the panel associated with the button is open. When a panel isn't open, --activeButton has a value of 0, and when it is, the value is 1.


          Direct usage of the variable

          On its own, the variable can be used to control things directly, like with opacity: var(--activeButton); or transform: scale(var(--activeButton)); which can be applied in the style="" attributes of SVG elements like paths, where a value of 0 will make the object disappear and 1 will make it show up again. This can either be an instantaneous change, or you can pair it with a transition to give it an animated appearance. More info on transition here: https://developer.mozilla.org/en-US/docs/Web/CSS/transition

          If you use transform: scale(var(--activeButton)); with a transition, you might notice the SVG element scaling up in an odd way that is relative to the top left corner of the icon. This happens because the scale is relative to the origin of the SVG, which is the top left corner by default. Luckily, you can adjust the position of the origin if the default behavior isn't what you are looking for with the property transform-origin. For a centered element, transform-origin: center; can be used, but you can also set specific x and y coordinates if necessary. More info on transform-origin here: https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin

          Sometimes it can also be necessary to have the opposite behavior of simply putting the --activeButton variable into a CSS property. For example, if you wanted an SVG element to disappear when a panel button is activated, then you couldn't use the variable on its own. In such cases, you can easily invert the variable and set a new variable to hold it like so: --activeButtonInverse: calc(1 - var(--activeButton));. Then using something like opacity: var(--activeButtonInverse); will let you accomplish your goal. If you are going to use the inverted variable for multiple elements, then it makes sense to set the variable definition on a parent group element, <g>, or even on the main <svg> element, so you don't need to repeat the inverted variable definition in the style="" attribute of each element that requires it.


          Using the variable with calc() for greater control

          CSS has many numerical properties that can be manipulated but have ranges that fall outside of the range between 0 and 1, so for the --activeButton variable to be useful with these properties, you need to use it within calc()s which allows you to use mathematical operators on the contents of the calc().

          Say you want to have a part of the icon move from the top of the icon to the bottom when the panel button is activated. To accomplish this, you would use transform: translateY();, but since SVG icons are suggested to fit in a 16x16 pixel area of a 28x28 pixel icon, you won't be able to move the icon element the entire distance without increasing the effect of the --activeButton variable like so: transform: translateY(calc(16px * var(--activeButton)));. So when the panel button isn't active (--activeButton is 0) the calc() will return 0px and not move the element, but once the button is activated and the variable is 1, the element will be moved 16px down in the icon.

          Using the calc() also allows you to convert the unitless variable into a pixel value by including the px on the multiplication factor. Depending on the CSS attribute you are manipulating, a unit of measurement or percentage value might be required.

          Another way you could use calc() with --activeButton is to provide a slight icon size increase to help indicate that the panel button is active. This could be accomplished with something like this: style="transform-origin: center; transform: scale(calc(0.2 * var(--activeButton) + 1)); transition: transform 0.1s ease-in;". This way, the scale will default to 1 when on an inactive panel button and increase to 1.2 times larger for an active one.


          Changing icon color with --activeButton

          Vivaldi suggests using the option currentColor for all fills and strokes in SVG icons to better allow the icon set to match the theme it is paired with, but when trying to indicate that a button is active, it can be beneficial to change the color from the normal white or black Foreground color to something more distinctive.

          By default, when you activate a panel button, it gets a bar along the side that uses your theme's Highlight color, so you might want to change the icon color to match. You can get the theme Highlight color with the CSS variable var(--colorHighlightBg), but you can't use a calc() alone to toggle between between 2 non-numerical values. That is where color-mix() can be useful. If you use something like this on the top level <svg> tag: style="color: color-mix(in srgb, var(--colorHighlightBg) calc(var(--activeButton) * 100%), currentcolor);", then you can control what ratio of the Highlight color and the Foreground (currentcolor) are mixed together to result in the final used color. By toggling between 0% and 100% of the Highlight color ratio, you can completely swap the color without any mixing. More info on color-mix() here: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix


          Example: History Panel icon with animated active indicator

          Preview:
          history-retro.gif

          Final SVG:

          <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" viewBox="0 0 28 28"
            style="--activeButtonInverse: calc(1 - var(--activeButton));">
            <path id="history-icon-blob-1" fill="#b193f0"
              style="transform: translateX(calc(-22px * var(--activeButtonInverse))); opacity: var(--activeButton); transition: 0.5s;"
              d="M8.83 24.72c9.97.79 14.37-7.1 11.77-13.82C17.2 2.13.32-1.47 2.84 7.3c1.69 5.9-.13 16.94 5.99 17.42Z" />
            <path id="history-icon-blob-2" fill="#ffd75a"
              style="transform: translateX(calc(26px * var(--activeButtonInverse))); opacity: var(--activeButton); transition: 0.5s;"
              d="M12.38 9.66c-18.1 1.38-10.38 8.21.72 12.12 9.26 3.26 11.92-1.24 12.35-7.01C26.68-1.8 20.8 9 12.38 9.66Z" />
            <path id="history-icon-star-1" fill="#fff"
              style="transform-origin: 20.4px 5.4px; transform: scale(var(--activeButton)); opacity: var(--activeButton); transition: 0.3s; transition-delay: 0.2s;"
              d="M20.45 2.2a3.2 3.2 0 0 1-3.2 3.2 3.2 3.2 0 0 1 3.2 3.2 3.2 3.2 0 0 1 3.2-3.2 3.2 3.2 0 0 1-3.2-3.2z" />
            <path id="history-icon-star-2" fill="#fff"
              style="transform-origin: 8.8px 23.5px; transform: scale(var(--activeButton)); opacity: var(--activeButton); transition: 0.3s; transition-delay: 0.4s;"
              d="M8.92 20.32a3.2 3.2 0 0 1-3.2 3.2 3.2 3.2 0 0 1 3.2 3.2 3.2 3.2 0 0 1 3.2-3.2 3.2 3.2 0 0 1-3.2-3.2z" />
            <g>
              <path fill="#fff" stroke="#fff" stroke-width=".5"
                d="m10.006 8.077.991 1.712a6.545 4.55 13.88 0 1 .338-.078l-.913-1.589Zm3.965.35v1.17a6.545 4.55 13.88 0 1 .338.046V8.46Zm3.537.315-.901 1.566a6.545 4.55 13.88 0 1 .304.135l.969-1.667Zm-11.085 1.51-.168.292 1.903 1.104a6.545 4.55 13.88 0 1 .203-.282zm15.433 0-2.839 1.644a6.545 4.55 13.88 0 1 .237.248l2.782-1.6-.169-.293zm-15.68 4.483.022.338h1.803a6.545 4.55 13.88 0 1-.136-.338Zm14.328 0a6.545 4.55 13.88 0 1 0 .338h1.352l.034-.338zM9.837 17.202l-3.312 1.915a1.352 1.352 0 0 0 .169.292l3.447-1.993a6.545 4.55 13.88 0 1-.304-.214Zm9.394.439a6.545 4.55 13.88 0 1-.292.225l2.534 1.465a1.318 1.318 0 0 0 .079-.338zm-7.265.71-.969 1.678h.394l.901-1.566a6.545 4.55 13.88 0 1-.326-.112zm4.63.45a6.545 4.55 13.88 0 1-.35.056l.733 1.285h.394zm-2.614.045v1.24h.338V18.89a6.545 4.55 13.88 0 1-.338-.045z" />
              <path stroke="#fd7100" stroke-width="1.126"
                d="m6.942 7.807 14.137 1.25a1.25 1.25 0 0 1 1.138 1.352l-.676 8.572a1.318 1.318 0 0 1-1.34 1.217l-12.46-.225a1.352 1.352 0 0 1-1.318-1.262l-.642-9.778a1.059 1.059 0 0 1 1.16-1.126Z" />
              <path fill="#f30000" d="m16.834 12.574-.546.14-2.42 1.876-.107 1.066 1.066-.11 1.87-2.425z" />
              <path fill="#f30000" d="m10.51 11.82.183.531 3.21 3.23 1.072.02-.196-1.053-3.713-2.636z" />
            </g>
          </svg>
          

          Design Notes:
          (WIP)...

          1 Reply Last reply Reply Quote 1
          Loading More Posts
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes
          Reply
          • Reply as topic
          Log in to reply
          • 1 / 1
          • First post
            Last post

          Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.

          Copyright © Vivaldi Technologies™ — All rights reserved. Privacy Policy | Code of conduct | Terms of use | Vivaldi Status