овёрнутый текст

Некоторое время назад я уже делился этим решением в соцсетях, но на этот раз хочу написать о нём немного подробнее.

Задача: получить повёрнутый на 90 градусов текст.

Проблема: широко известно, что при использовании transform, блок ведёт себя аналогично сдвигу через position:relative — продолжает занимать место в потоке по состоянию до трансформации, так что, по факту, изменение происходит только визуальное.

Однако, довольно часто можно захотеть повернуть блок так, чтобы этот поворот влиял и на поток. Например, если мы захотим выстроить несколько вертикальных блоков в ряд, подобно книгам на полке, или если мы захотим использовать повёрнутый текст в качестве заголовков таблицы — в этих случаях нам нужно будет гарантировать, что высота станет равна ширине блока, в то время как ширина — высоте.

У меня получилось решить эту задачу с одним допущением: нужно знать высоту поворачиваемого элемента. В этом случае реализация становится очень простой:

  1. Нам понадобится дополнительный элемент. HTML каждого блока будет примерно таким:

    <span class="rotated-text">
        <span class="rotated-text__inner">
            Rotated foo
        </span>
    </span>
    
  2. Врапперу мы задаём вот такие стили:

    .rotated-text {
        display: inline-block;
        overflow: hidden;
    
        width: 1.5em;
        line-height: 1.5;
    }
    

    Здесь мы делаем элемент инлайн-блочным (это не критично, сработало бы и блочное отображение, но инлайн-блок чаще бывает нужен), затем обрезаем все выступающие части (пригодится позже), далее задаём ширину, равную текущей высоте блока, — то самое допущение (line-height тут приведён в качестве примера того, что сейчас определяет высоту блока, а так как все элементы, используемые в примере, однострочные, то это и будет его высотой).

  3. Внутренний элемент делаем инлайн-блочным, чтобы его ширина схлопнулась по контенту. После чего задаём white-space:nowrap для того, чтобы ничего никуда не переносилось (ведь выше мы ограничили ширину), ну и поворачиваем блок, считая верхний левый угол точкой отсчёта (для читаемости свойства приведены без префиксов):

    .rotated-text__inner {
        display: inline-block;
        white-space: nowrap;
    
        transform: translate(0,100%) rotate(-90deg);
        transform-origin: 0 0;
    }
    
  4. А теперь самое главное: нам нужно сделать этот внутренний элемент «квадратным» — это сделает высоту конечного элемента равной ширине, ну а ширина у нас обрезается враппером. Для того, чтобы сделать наш элемент квадратным, я применяю вот такой вот трюк:

    .rotated-text__inner:after {
        content: "";
        float: left;
        margin-top: 100%;
    }
    

    Вроде всё довольно просто, но немногие знают о том, что вертикальные маджины и паддинги, заданные в процентах, исчисляются не от высоты блока, а от его ширины. Это поведение редко используется на практике, но у нас как раз тот случай, когда оно пригодилось.

В итоге мы получаем квадратный элемент, обрезаемый враппером до ширины своей прошлой высоты, такой элемент можно использовать в любом контексте, для него будут работать разные свойства вроде text-align или vertical-align, так что можно сказать, мы получили «честный» повёрнутый блок.

Ну и несколько живых примеров:

аголовки таблиц

Очевидный пример — компактные по ширине заголовки таблиц.

First th Some cell And another
Second th 12 12314
Third th 12 12314
First th The Second th Third th
2 4 42
12 4 234
13 100 0

«Книжная полка»

Так как повёрнутые блоки получаются «честными», то, если выстроить их в ряд, высота ряда будет равна высоте самого большего из них:

Как-то так.

Ещё раз: мы должны знать высоту блока, так что, если понадобится поворачивать таким образом многострочные блоки, нужно будет соответствующим образом изменять ширину их врапперов.

Кроме того, возможно, этот метод можно заставить работать и в IE — применив матричный фильтр для поворота и добавив элемент в замену псевдоэлементу. Мне лень всё это делать, но вы можете всегда попробовать это сделать сами :)


Вы можете прокомментировать эту статью в Мастодоне.