Рассмотрим новое объявление aspect-ratio
(соотношение сторон) и его использование. Una Kravets написала вводную статью, но следует отметить некоторые дополнительные технические моменты. В конце будет предложен небольшой запасной вариант, который можно использовать, если вам прямо сейчас нужно пользоваться свойством aspect-ratio
.
На момент написания aspect-ratio
поддерживается в Chrome 90, Safari Technology Preview и Firefox 88, если установить флаг aspect-ratio в about: config. Вам понадобится один из этих браузеров, чтобы увидеть примеры ниже, за исключением запасного, который должен работать во всех браузерах, поддерживающих настраиваемые свойства.
Слабая декларация
Значение CSS-свойства aspect-ratio
– слабая декларация. Если у контейнера указаны ширина и высота, браузер будет использовать эти значения и проигнорирует указанное значение соотношения сторон. Ширина и высота могут декларироваться с помощью явных значений width
и height
или иными способами.
aspect-ratio
В этом примере для всех контейнеров в CSS указано соотношение сторон 16:9 – aspect-ratio: 16/9
. Для первого контейнера ширина и высота не указаны (width: auto; height: auto;
т.е. ширина и высота в обычных условиях будут определяться содержимым). Тогда свойство aspect-ratio
возьмёт фактическую ширину в пикселях и применит указанное в значении соотношение сторон для вычисления высоты.
Для второго контейнера указана высота height: 50px;
, ширина по-прежнему не определена (width: auto;
). Высота усилилась, но автоматическая ширина всё ещё слаба и позволяет свойству aspect-ratio
переопределить её. Таким образом, теперь ширина поля рассчитывается исходя из значений высоты (height
) и соотношения сторон (aspect-ratio: 16/9
).
Для третьего контейнера указаны фиксированные значения ширины width: 150px;
и высоты height: 100px;
, а также соотношение сторон aspect-ratio: 16/9
. Но теперь и ширина, и высота усилены, поэтому значение 16/9
свойства aspect-ratio
будет игнорироваться и указанное соотношение сторон не будет применяться.
Здесь и далее в примерах слайдером над контейнером можно изменять его ширину, чтобы посмотреть, что происходит с блоками внутри него.
Округление
Даже когда aspect-ratio
работает нормально, браузер должен вычислить целое число пикселей устройства для ширины и высоты контейнера. Дробные части отсекаются, поэтому рассчитанное соотношение сторон контейнера не может бывает точным на 100%.
В примерах в этом тексте, можно часто увидеть узкую красную полосу, выглядывающую из-под фонового изображения. Это изображение имеет такое же соотношение сторон, что и контейнер, в котором оно размещено, но, очевидно, использует другой способ вычисления. В обычных условиях эти крошечные различия часто не видны невооруженным глазом, поэтому их можно игнорировать.
Однако жирные красные полосы, как в последнем квадрате в первом примере, указывают на наличие проблемы с вычислением aspect-ratio
.
min- и max-width и -height
grid aspect-ratio и min- и max-height
Вы можете указать min/max-width/height
(минимальные/максимальные размеры) для контейнеров. Они применятся, как обычно и указанное значение aspect-ratio
тоже будет применено. В этом примере у первого контейнера min-height: 100px
, у второго – max-height: 50px
, а у третьего – min-width: 100px
. Как видно (подвигайте слайдер), они растягиваются вверх или вниз до определенного максимума и минимума и при этом соблюдают указанное для них значение соотношения сторон.
box-sizing: border-box
Теперь мы подобрались к более сложной теме, изначально открытой Ana Tudor – и всю эту статью лучше прочитать.
Когда aspect-ratio
работает нормально, ширина или высота контейнера будет изменяться, чтобы соответствовать другой стороне и указанному значению соотношения сторон. Однако точный эффект будет зависеть от того, как реальные ширина и высота определяются свойством box-sizing
.
width
может возвращать ширину только содержимого без отступов (padding
) и границы (при box-sizing: content-box;
по умолчанию) или ширину контента + padding
+ border
(при box-sizing: border-box
). В общем, последнее – то, что нужно.
aspect-ratio и box-sizing
В следующем примере контейнерам добавлены отступы: padding:10%
. Процентное значение всегда рассчитывается относительно ширины родительского элемента. Таким образом, у этих контейнеров будут отступы в 10% от ширины их родительских элементов, даже сверху и снизу.
Поскольку отступы одинаковы со всех четырех сторон, это может нарушить соотношение сторон контейнера. Это зависит от определения box-sizing
для контейнера. Если используется box-sizing: content-box
(по умолчанию), то у ширины и высоты будет правильное соотношение сторон, но они при этом определяют только область содержимого. Со всех сторон будет добавляться равное количество отступов, и соотношение сторон нарушится.
Эту проблему легко решить, если объявить box-sizing: border-box
. Теперь ширина и высота определяют всё вместе: контент, отступы и границу. Всей этой области будет задано правильное соотношение сторон. Таким образом, отступы (padding
) легко интегрируются с правильным соотношением сторон.
Вообще, правильнее всегда устанавливать box-sizing: border-box
для всего сайта, это сильно упрощает работу с размерами элементов, и как правило устраняет ряд подводных камней. Значение content-box
было ошибкой, как признал сам W3C (еще в 2002 году). Тот факт, что он влияет на aspect-ratio
, просто ещё раз это подтверждает.
apect-ratio в flexbox
В flex- или grid-контексте может показаться, что aspect-ratio
не работает. Фактически, именно эти проблемы и побудили написать эту статью.
Посмотрите на пример ниже. Не работает! О, паника! Что происходит?
flex aspect-ratio
Здесь происходит нормальное поведение flexbox по умолчанию. Сначала рассчитывается ширина элементов (исходя из установленных flex-base: 30%; grow: 1
), а после этого устанавливается высота всех элементов. Эти высоты рассчитываются путем применения соотношения их сторон, но самый высокий контейнер используется для установки высоты всех элементов в строке. В примере выше это контейнер 1/1
, поэтому у остальных контейнеров 16/9
и 4/3
такое же соотношение сторон 1/1
.
По умолчанию для flexbox таким поведением управляет align-items: stretch
.
Если будете использовать любое другое значение, высота блоков будет установлена автоматически и у них будет правильное соотношение сторон. flex-start
– наиболее очевидный выбор, а дополнительные параметры можно найти в отличном руководстве по flexbox на CSS Tricks.
flex aspect-ratio и align-items
Если по какой-то причине потребуется отменить растяжение по высоте только для одного элемента, для него можно указать align-self: flex-start
или любое другое значение, либо height: min-content
. Оба варианта будут работать нормально. Для контейнера 16/9 в примере ниже указано height: min-content
.
flex, aspect-ratio и height: min-content
aspect-ratio и grid
В grid-контексте aspect-ratio
сталкивается с теми же проблемами, что и в среде flexbox, только еще и с ошибками браузера. Можно ожидать такого же поведения, что и в контексте flexbox, но на деле происходит не совсем то же самое.
Хорошая новость в том, что align-items
, align-self
и height: min-content
работают точно так же, как с flexbox. Они отменяют стандартное поведение для grid, когда высота всех элементов в строке растягивается до самого высокого из них.
grid, aspect-ratio и align-items
Проблемы заключаются в рендеринге по умолчанию с aspect-ratio
. Chrome и Safari реализуют это одним неправильным способом, а Firefox – совершенно другим способом, но тоже неправильным.
Если у всех контейнеров в приведенном ниже примере будет одинаковая ширина и высота, это устранит ошибки. Одинаковая высота у них по причинам, которые объяснялись выше в разделе flexbox, а grid в этом отношении (можно надеяться) работает так же.
grid aspect-ratio с багами браузера
Firefox
подчиняется aspect-ratio
, но скорее всего – нет. Вместо этого, как в flexbox-примере, он должен растягивать блоки до высоты самого высокого в строке. Вдобавок он вычисляет высоту всей сетки, предполагая, что у её элементов есть минимальная высота, необходимая для их содержимого, но во всех примерах выше нет контента, т.е. его высота равна 0. Таким образом, grid-контейнер слишком мал. Оба случая явно ошибочны, и вероятно, скоро будут исправлены.
Chrome и Safari TP правильно определяют размер контейнера в grid, но похоже, принимают высоту в качестве справочной и соответственно изменяют ширину, вместо того, чтобы основываясь на ширине, изменять высоту. Таким образом, элементы 16/9 и 4/3 становятся слишком широкими. Вероятно, это тоже ошибка, а если это не так, требуются пояснения, что тут происходит. К счастью, эта ошибка исчезает, если как обычно, использовать align-items
.
Запасной вариант
“Написав этот запасной вариант, я обнаружил, что Ana Tudor написала примерно то же самое в своей статье. Я имею в виду, почему я вообще пытаюсь соревноваться с такими ужасно умными людьми, как она? Но я пришел к этому самостоятельно, честно.” – Peter-Paul Koch
Поскольку поддержка браузеров ещё не совсем завершена, придётся продолжать использовать запасной вариант – старый трюк с padding-top
, как сделано в этом абзаце. Предполагается, что у него будет соотношение сторон даже в браузерах, которые не поддерживают aspect-ratio
.
Ядро запасного варианта – это CSS, написанный ниже. Справедливое предупреждение: это решение только слегка протестировано; от замысла к успешному выполнению потрачено примерно 30 минут, хотя ещё 90 минут ушло на проблему с настраиваемыми свойствами.
Дополнительный <span>
уродлив, но необходим. Если ваши контейнеры с пропорциональным соотношением сторон не содержат текста, вы можете его убрать.
<p class="test"><span>Текст, текст, текст.</span></p>
p.test {
--aspectRatio: 16/9;
--padding: 0.5em;
border: 1px solid;
padding: var(--padding);
aspect-ratio: var(--aspectRatio);
box-sizing: border-box;
}
@supports not (aspect-ratio: 16/9) {
p.test {
padding: 0;
padding-top: calc((1 / (var(--aspectRatio)))*100%);
position: relative;
}
p.test > span {
display: block;
position: absolute;
top: var(--padding);
left: var(--padding);
}
}
Сохраните желаемое соотношение сторон в переменной --aspectRatio
. Установите это значение свойству aspect-ratio
. Если браузер не поддерживает aspect-ratio
, установите для padding-top
значение 1 / --aspectRatio
в процентах. Сценарий, выполняемый на этой странице, изменяет значение CSS-переменной --aspectRatio
.
Хитрость здесь в том, что процентное значение рассчитывается относительно ширины родительского элемента. Если блок занимает всю ширину своего родителя, вы можете взять эту ширину, умножить ее на 1, разделенное на соотношение сторон (например, 9/16, когда соотношение сторон составляет 16/9) и преобразовать результат в проценты. Теперь отступ растягивает прямоугольник до желаемого соотношения сторон.
Если в блоке есть реальное содержимое, его придётся обернуть дополнительным HTML-элементом и присвоить этому элементу position: absolute
, чтобы он не влиял на высоту блока. Затем его надо разместить в контейнере с координатами сверху и слева, равными значению padding
. Теперь текст кажется естественным. Этот трюк не понадобится, если в поле есть только фоновое изображение или градиент; они в любом случае игнорируют отступы (padding
).
Или можно подождать несколько месяцев, когда все браузеры будут поддерживать aspect-ratio
. Это не займет много времени.
А о невероятном количестве скобок, которые нужны в строке с padding-top
можно вообще написать отдельную статью.
оригинал статьи aspect-ratio на сайте Peter-Paul Koch QuirksMode.org.