Search code examples
htmlcss

How to display a range input slider vertically


I would like to display an <input type="range" /> slider control vertically. I'm only concerned with browsers that support the range slider control.

I've found some comments and references that seem to indicate that setting the height greater than the width will cause the browser to change the orientation automatically, but in my testing, that only works in Opera used to work in Opera, but not anymore. How can I orient an HTML5 range slider vertically?


Solution

  • As of April 2024 for Chrome (et al), and November 2023 for Firefox, use writing-mode: vertical-lr (or vertical-rl) and direction: rtl. (Using direction: ltr would result in sliding up to get a low value, and sliding down to get a high value.)

    For older versions of Chrome (and other older chromium based browsers), use appearance: slider-vertical and set a width. width: 16px matches the default for newer browsers using writing-mode.

    For older versions of Firefox, add an orient="vertical" attribute to the html.

    input[type=range][orient=vertical] {
        writing-mode: vertical-lr;
        direction: rtl;
        appearance: slider-vertical;
        width: 16px;
        vertical-align: bottom;
    }
    <input type="range" orient="vertical" />

    I set the vertical-align because without it (or with vertical-align: baseline), you're going to get layout differences between older browsers and newer browsers. To see what I mean, run this snippet:

    body {
      font-family: sans-serif;
    }
    
    body>div {
      margin: 4px;
      padding: 4px;
      float: left;
    }
    
    div div {
      border: 1px solid #ccc;
    }
    
    input[type=range] {
      width: 16px;
    }
    
    input[type=range][orient=vertical] {
      appearance: slider-vertical;
    }
    
    input[type=range]:not([orient=vertical]) {
      writing-mode: vertical-lr;
      direction: rtl;
    }
    
    .v-baseline input[type=range] {
      vertical-align: baseline;
    }
    
    .v-top input[type=range] {
      vertical-align: top;
    }
    
    .v-middle input[type=range] {
      vertical-align: middle;
    }
    
    .v-bottom input[type=range] {
      vertical-align: bottom;
    }
    <div>
      default:
      <div class="v-baseline">
        <input type="range" orient="vertical" />
        <input type="range" />
      </div>
    </div>
    <div>
      top:
      <div class="v-top">
        <input type="range" orient="vertical" />
        <input type="range" />
      </div>
    </div>
    <div>
      middle:
      <div class="v-middle">
        <input type="range" orient="vertical" />
        <input type="range" />
      </div>
    </div>
    <div>
      bottom:
      <div class="v-bottom">
        <input type="range" orient="vertical" />
        <input type="range" />
      </div>
    </div>

    If you only need to support the very latest browser versions (or if it's about 2026 or later), you can pare it down:

    input[type=range] {
        writing-mode: vertical-lr;
        direction: rtl;
        vertical-align: middle;
    }
    <input type="range" />


    This latest change by the browser implementors (and thus the standards body) is interesting. My opinion, for whomever is interested, is that appearance: slider-vertical was semantically better. Actually, I do think there is some logic to the idea that the default orientation of a slider would follow the text flow. But, I think modifying the writing-mode in order to change the orientation, is wonky and unintuitive and semantically incorrect. A document with writing-mode: horizontal-tb to suddenly switch to vertical-*, and not because we've switched languages, doesn't make sense. As soon as you start to do something like input[type=range]::before { content: "my label" }, it becomes more obvious why it doesn't make sense. Now, to get our text oriented correctly, we have to override the control's value back to the document's value. If it were up to me, I'd control orientation with a distinct attribute, like orient: vertical. And the default value of the orient attribute would come from the writing-mode.