Я всегда считал, что с flexbox довольно легко работать — глоток свежего воздуха после стольких лет float'ов и clearfix'ов.


Правда недавно я обнаружил что борюсь с ним; что-то растягивалось, когда я не думал, что оно должно тянуться. Я поправил здесь, другой элемент сжался. Я починил это, что-то другое ушло за экран. Какого Джорджа Буша тут происходит?


В конце концов, все заработало, но солнце село, а мой процесс был привычной игрой с CSS. Или… как называется та игра, где надо ударить крота, а затем другой крот выпрыгивает и надо ударить и его тоже?


Как бы там ни было, я решил что пора вести себя как взрослый разработчик и выучить flexbox должным образом. Но вместо того, чтобы прочитать 10 очередных блог-постов, я решил отправиться прямиком к исходнику и прочитать The CSS Flexible Box Layout Module Level 1 Spec


Вот хорошие отрывки.



1. Margin обладает особыми силами


Я думал, что если, например, ты хочешь заголовок с логотипом и названием сайта слева, а кнопкой логина справа...



… тебе следует дать названию flex: 1, чтобы прижать остальные элементы к другому концу строки.


Вот почему flexbox — Очень Хорошая Вещь. Простые вещи такие простые.


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


Отличные новости! Вместо этого, ты можешь сказать прямо: «прижми этот элемент вправо», определив margin-left: auto на нужном элементе. Думай об этом как о float: right.


Например, если элемент слева является изображением:



Мне не нужно применять flex к изображению, мне не нужно применять space-between к flex-контейнеру, я просто установлю margin-left: auto на кнопке «Войти» («Sign in»):


.header {
  display: flex;
}
.header .logo {
  /* nothing needed! */
}
.header .sign-in {
  margin-left: auto;
}

Тебе может показаться это некоторым хаком, но нет, это прямо там в обзоре спецификации как способ прижать flex-элемент в конец flexbox'а. У способа даже есть своя глава: "Выравнивание с авто margin'ами".


О, мне также следует здесь упомянуть, что я предполагаю flex-direction: row везде в этом блог-посте, но все применимо также и к row-reverse или column или column-reverse.



2. min-width имеет значение


Возможно, ты думаешь, что несложно заставить все flex-элементы внутри flex-контейнера сжиматься, для того чтобы уместить контент. Наверняка, если ты укажешь flex-shrink: 1 на элементах, они так и будут себя вести, правда?


Может, пример.


Скажем, у тебя есть часть DOM, которая отображает книгу на продажу и кнопку чтобы ее купить.


Любопытная бабочка
Спойлер: бабочка умрет в конце.

Ты разместил все с помощью flexbox и все хорошо.


.book {
  display: flex;
}
.book .description {
  font-size: 30px;
}
.book .buy {
  margin-left: auto;
  width: 80px;
  text-align: center;
  align-self: center;
}

(Поскольку ты хочешь кнопку «Купить» справа — даже для очень коротких названий — ты, будучи умным, указал margin-left: auto)


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


И потом получаешь неприятный сюрприз. Совсем не хорошего рода.


Некий дурень, много о себе возомнивший, написал книгу с длинным словом в названии.



Все сломано!


Если красная граница обозначает ширину смартфона, и ты скрываешь переполнение (overflow: hidden), ты только что потерял свою кнопку «Купить». Твой коэффициент конверсии — как и эго бедного автора — будет страдать.


(Примечание: к счастью, там где я работаю, есть хорошая QA команда, которая наполнила нашу базу данных разнородным текстом, наподобие такого. В частности, именно эта проблема побудила меня прочитать спецификацию.)


Оказывается, такое поведение происходит из-за того, что min-width элемента описания изначально установлена в auto, что в данном случае равняется ширине слова Electroencephalographically (электроэнцефалографически). Flex-элементу буквально не разрешается быть уже чем это слово.


Решение? Переопределить эту проблемную минимальную ширину min-width: auto установив min-width: 0, указывая flexbox'у, что этот элемент может быть уже, чем содержимое внутри него.


Теперь за управление текстом внутри элемента отвечаешь ты. Я предлагаю перенести слово. Таким образом, твой CSS будет выглядеть так:


.book {
  display: flex;
}
.book .description {
  font-size: 30px;
  min-width: 0;
  word-wrap: break-word;
}
.book .buy {
  margin-left: auto;
  width: 80px;
  text-align: center;
  align-self: center;
}

Результат будет таким:



Опять же, min-width: 0 не какой-то хак для обхода нелепости, это предлагаемое поведение прямо в спецификации.


В следующем разделе, я вернусь к тому, что кнопка «Купить» совсем не 80 пикселей по ширине, как я довольно ясно ей сказал.



3. Авторы flexbox обладают хрустальным шаром


Как вы возможно знаете, свойство flex является краткой записью flex-grow, flex-shrink и flex-basis.


Должен признать, я потратил энное количество минут, гадая-проверяя различные значения для этой тройки, когда пытался заставить элементы тянуться так, как надо мне.


Что я не знал до сей поры, это то, что, в общем случае, я хочу одну из трех комбинаций:


  • Если я хочу, чтобы элемент немного сжимался, когда места недостаточно, но не тянулся шире чем ему надо: flex: 0 1 auto
  • Если мой flex-элемент должен тянуться для заполнения всего доступного пространства, и немного сжиматься если места не хватает: flex: 1 1 auto
  • Если мой элемент не должен менять размеры совсем: flex: 0 0 auto

Надеюсь, что ты пока не на максимальном изумлении — сейчас станет еще поразительнее.


Видишь ли, Бригада Flexbox'а (мне нравится думать, что команда flexbox'а носит кожаные куртки с этой надписью сзади — доступны мужские и женские размеры). Где там было это предложение? Ах да, Бригада Flexbox'а знала, что я хочу эти три комбинации свойств в большинстве случаев. Поэтому они дали им ключевые слова специально для меня.


Первый случай — это значение initial так что ключевое слово не нужно. Для второго случая используется flex: auto, и flex: none замечательно простое решение чтобы элемент не тянулся совсем.


Кто бы мог подуть! (Who woulda thunk it — игра слов, прим. переводчика)


Это как если бы было box-shadow: garish, что по умолчанию равнялось 2px 2px 4px hotpink потому что считалось «полезным значением по умолчанию».


Вернемся к невероятно уродливому книжному примеру. Чтобы сделать ту кнопку «Купить» стабильно широкой для попадания пальцем...



… мне всего лишь надо задать на ней flex: none:


.book {
  display: flex;
}
.book .description {
  font-size: 30px;
  min-width: 0;
  word-wrap: break-word;
}
.book .buy {
  margin-left: auto;
  flex: none;
  width: 80px;
  text-align: center;
  align-self: center;
}

(Да, я мог бы указать flex: 0 0 80px; и сэкономить строку CSS. Но есть что-то особенное в том, как ясно flex: none демонстрирует намерение кода. Это хорошо для Будущего Дэвида который забудет как это все работает.)



4. Есть такая вещь inline-flex


По правде говоря, я узнал, что есть такая вещь как display: inline-flex несколько месяцев назад. И то, что она создаст инлайновый flex-контейнер, вместо блочного.


Но по моей оценке, 28% людей еще не знали этого, так что… теперь знайте, нижние 28%.



5. vertical-align не влияет на flex-элемент


Возможно это то, что я знал наполовину, но я уверен, что в какой-то момент, когда пытался задать правильное выравнивание, я мог испробовать vertical-align: middle и пожать плечами, когда это не сработало.


Теперь я знаю наверняка, прямо из спецификации, что "вертикальное выравнивание не влияет на flex-элемент" (так же как и float, замечу).



6. Не используй margin или padding в %


Это не просто уровня «лучшая практика», это уровня «совет-от-бабушки», так что просто делай что говорят и не задавай вопросов.


«Авторам следует полностью избегать использования процентов в padding'ах или margin'ах на flex-элементах» — с любовью, спецификация flexbox.


За этим следует моя самая любимая цитата из всех, когда-либо существовавших, спецификаций:


Заметка: это разночтение отстой, но оно в точности отражает текущее состояние мира (нет консенсуса среди реализаций, и нет консенсуса внутри CSSWG)...

Осторожно! Бомбардировка честностью продолжается.



7. Margin'ы соседних элементов не схлопываются


Возможно, ты уже знаешь, что margin'ы иногда объединяются вместе. Ты также можешь знать, что margin'ы не объединяются вместе в некоторых других случаях.


И теперь мы все знаем, что margin'ы соседних flex-элементов никогда не объединяются.



8. z-index работает даже если position: static


Я не уверен, что мне есть дело до этого. Но я чувствую, что в один день, может быть, это пригодится. Это в точности та же причина, по которой я держу бутылку лимонного сока в холодильнике.


Однажды в моем доме будет другой человек, который скажет типа «эй, у тебя есть лимонный сок?», а я типа «конечно, в холодильнике», а он «спасибо, приятель. Эй, а надо ли мне указывать position если я хочу задать z-index на flex-элементе?», и тут я такой «не братан, не для flex-элементов».



9. Flex-basis тонкое и важное свойство


Когда твои требования перерастут ключевые слова initial, auto и none, все станет немного сложнее, и теперь, когда я понял flex-basis, забавно, знаешь, я не могу придумать как закончить это предложение. Оставь комментарий, если у тебя есть идеи.


Если у тебя есть три flex-элемента с flex-значениями 3, 3, 4, тогда они гарантированно займут 30%, 30% и 40% доступного пространства, независимо от их содержимого, если их flex-basis равен 0. И только если он равен нулю.


Тем не менее, если ты хочешь чтобы flex вел себя в более дружественной, предсказуемой манере, используй flex-basis: auto. В этом случае flexbox примет твои flex-значения во внимание, но также учтет и другие факторы, подумает немного, и подберет ширины, подходящие по его мнению тебе.


Взгляни на эту четкую диаграмму из спецификации:



Я уверен, что это упомянуто по меньшей мере в одном из блог-постов про flex, которые я читал, но по какой-то причине, не проникся пока не увидел эту картинку в спецификации (schmick pick in the spec)(тройная рифма если ты из Новой Зеландии).



10. align-items: baseline


Когда я хотел выравнять flex-элементы по вертикали, я всегда использовал align-items: center. Но также как с vertical-align, у тебя есть возможность установить значение в baseline, что может быть более подходящим если у твоих элементов различный размер шрифта, а ты хочешь выравнять их базу.


Возможно очевидно, align-self: baseline тоже работает.



11. Я довольно глуп


Сколько бы раз я не читал следующий параграф, я остался неспособным его понять...


Размер содержимого — это минимальный размер содержимого на основной оси, зажатый, если он имеет соотношение сторон, с помощью любых определенных минимальных и максимальных свойств кросс-размера, преобразованных через соотношение сторон, а затем дополнительно зажатых с помощью свойства максимального основного размера, если это определено.

Оригинальный вариант
The content size is the min-content size in the main axis, clamped, if it has an aspect ratio, by any definite min and max cross size properties converted through the aspect ratio, and then further clamped by the max main size property if that is definite.

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


Как будто Минни Маус и Мэд Макс завели ребенка семь лет назад, и теперь он, будучи пьяным с мятного шнапса, оскорбляет всех в пределах слышимости словами, которые он узнал когда Мамочка и Папочка ругались.


Леди и Джентльмены, мы начали наш спуск в чепуху, что значит пришло время подвести итоги (или перестань читать, если ты здесь для изучения нового).




Самое интересное из того, что я узнал, читая спецификацию, это то, насколько неполным было мое понимание, несмотря на полдюжины блог-постов которые я прочитал, и на то, насколько относительно простым является flexbox. Оказывается, что «опыт» — это не просто занятие одним и тем же из года в год.


С удовольствием могу отметить, что время, потраченное мной на чтение, уже окупилось. Я прошелся по старому коду, выставил авто margin'ы, flex-значения в краткой записи auto или none, и задал минимальную ширину в ноль там, где это было нужно.


Я лучше отношусь к этому коду теперь, зная что я делаю это должным образом.


Еще я узнал, что несмотря на то, что спецификация — местами — перенасыщена и предназначена для вендоров как я и думал, все же содержит много дружественных слов и примеров. В ней даже выделены части которые скромные веб-разработчики могут пропустить.


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


Теперь, если позволите, мне надо идти и прочитать все остальные CSS спецификации.


P.S. Я крайне рекомендую прочитать следующий список всех flexbox багов по браузерам:
github.com/philipwalton/flexbugs




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


Оригинальный пост здесь: hackernoon.com/11-things-i-learned-reading-the-flexbox-spec-5f0c799c776b

Поделиться с друзьями
-->

Комментарии (25)


  1. den_golub
    30.05.2017 18:22

    Сколько бы раз я не читал следующий параграф, я остался неспособным его понять…

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

    Оригинальный вариант
    The content size is the min-content size in the main axis, clamped, if it has an aspect ratio, by any definite min and max cross size properties converted through the aspect ratio, and then further clamped by the max main size property if that is definite.


    Размер содержимого — это минимальный размер содержимого на основной оси, зажатый, если он имеет соотношение сторон, с помощью любых определенных минимальных и максимальных свойств кросс-размера, преобразованных через соотношение сторон, а затем дополнительно зажатых с помощью свойства максимального основного размера, если это определено.

    Так лучше?


    1. Checkmatez
      30.05.2017 18:31

      Да, поправил, спасибо.


      1. sshikov
        30.05.2017 22:08

        Мне показалось, или вы пытаетесь переводить названия свойств CSS? cross — это из CSS, это размер по другой оси. Тут не надо пытаться перевести красиво, тут надо понять, о каких свойствах шла речь, и привести ссылки на них. Или пояснить. Иначе и оригинал, и перевод будут одинаково непонятны.

        Ну т.е., если у вас есть ограничение размера по высоте, и указан аспект ratio (пропорция x/y), то ограничение размера по высоте будет влиять на ширину, так как описано — т.е. сначала применяются ограничения по другой оси, с учетом пропорции, а потом ограничения по оси основной.


    1. PFight77
      31.05.2017 20:20

      Слово "зажатый" режет слух. Почему не "сжатый"?


  1. mwambanatanga
    30.05.2017 18:46
    +3

    Что во имя Джорджа Буша происходит?

    Не «во имя», а наоборот, «что за чертовщина происходит?». Джордж Буш имел, знаете ли, несчастье прослыть дьяволом.


    1. Checkmatez
      30.05.2017 19:26

      Наверное, вы правы. Но пожалуй, оставлю упоминание Буша, как в оригинале.


      1. mwambanatanga
        31.05.2017 09:39
        +7

        В оригинале не написано «во имя». «What in the George W Bush ...» это производное от обычного английского «What in the hell ...». Если очень хочется оставить это выражение (кстати, а в чём его ценность в контексте статьи?), то как-то типа «что за джорджбушевщина творится?».


        1. Checkmatez
          31.05.2017 20:47

          Поправил, спасибо. Ценности особой нет, есть желание держаться поближе к оригиналу в переводе. К сожалению, такие афоризмы теряются в переводе…


  1. xtala
    30.05.2017 21:03

    Спасибо, полезная статья, добавил в закладки. Правда все еще как-то с опаской к флексбоксу отношусь в плане его поддержки браузерами. Судя по результатам https://caniuse.com/#search=flex у флексбокса есть 8 проблем в браузерах Safari 10, Firefox 51, IE 10, IE 11. Понятно, что доля этих браузеров сейчас очень мала, но все же я думаю пока рано скидывать со счетов тот же IE 11.


    1. almazmusic
      30.05.2017 23:36

      https://github.com/philipwalton/flexbugs — ну так для этого сделали postcss. Вам ничего не мешает внедрить его в проект, ничего не сломав, т.к. он модульный.


  1. dom1n1k
    30.05.2017 22:06
    +4

    Правда недавно я обнаружил что борюсь с ним

    Абсолютно те же впечатления. С одной стороны, флексбокс конечно же дал много возможностей, о которых раньше не мечтали. С другой, во многих случаях он работает не вполне однозначно и предсказуемо. Он вернул в верстку элемент борьбы и шаманства. Как было 10 лет назад — сверстал и полез смотреть в разные браузеры, где что уползло. А уже позднее флоаты были хоть и скудным, но абсолютно надежным средством — как задумал, так и работает. И вот флекс решил это исправить.


    1. PFight77
      31.05.2017 20:18
      +1

      В основном шаманство с флексами возникает от непонимания флексов. Они действительно не такие простые, как пишут во всяких "полных руководствах по флексбокс" (например, там обычно не пишут всего того, что в этой статье). Есть конечно и баги в разных браузерах, но их не так много, с этим можно жить. Поэтому, думаю некорректно сравнивать флексы с тем что было раньше.


      1. dom1n1k
        31.05.2017 20:43

        А почему бы и не сравнивать? Лично я считаю, что флексы недопродуманы именно на уровне спецификации (баги в браузерах тоже есть, но это вторично). Базовая идея вроде и хороша, но в реальной практике всплывает много шероховатостей. Причем как тонких нюансов, так и вполне себе лежащих на поверхности. Использую в продакшене флексы около года наверное, вроде бы достаточно, чтобы прочувствовать.

        Я помню, как Вадим Макеев говорил, что «это первая система раскладки, которая не хак». Задумывалась именно так. По факту хаками всё равно попахивает :)

        Возлагаю большие надежды на гриды, но думаю, что до реально широкого прода они доползут только года через джва. А до того мы будет наблюдать борьбу консерваторов и полифилеров.


        1. PFight77
          31.05.2017 22:10

          Ужасно интересно, приведите примеры шероховатостей. Я тоже использую флексы, но обычно для не очень сложных вещей. Кроме min-width и flex-basis вроде пока явных сюрпризов не встречал.


          1. dom1n1k
            01.06.2017 00:20

            Прежде всего, недоработан перенос (flex-wrap: wrap). Очень не хватает возможностей ручного переноса и, наоборот, запрета переноса. Ну то есть блочные аналоги br/nobr.
            Или например проблема выравнивания последней строки (на SO куча вопросов на эту тему).


  1. vsb
    31.05.2017 13:30

    Для вёрстки мобильных приложений на React Native пользуюсь флексбоксом — просто песня, как будто специально для этого его придумали. Круче только constraints из Cocoa


  1. mrwoo
    31.05.2017 20:48

    +100500 в карму.


    1. Checkmatez
      31.05.2017 20:52

      Спасибо.


  1. XRSD
    31.05.2017 20:48

    «В следующей секции»

    В оригинале: «In the next section». Небольшая поправка: «section» переводится как «параграф», «раздел», если речь идет о книге, статье (или «колонка», «рубрика» — если о газете и т. д.)


    1. Checkmatez
      31.05.2017 20:50

      Спасибо, поправил.


  1. Movimento5Litri
    31.05.2017 20:50

    Когда речь идёт о вебе, чтение стандартов вызывает несварение…


    1. Checkmatez
      31.05.2017 20:51

      Можете пояснить почему? Свежие стандарты вполне себе читабельны.


      1. Movimento5Litri
        01.06.2017 08:43
        -1

        Потому что их не читают разработчики браузеров


  1. Vallek
    02.06.2017 17:04

    Про перевод. Очень всегда корежит от обращения к читателю на «ты». Тем более тут перевод с английского.


    1. mrwoo
      07.06.2017 18:48

      Ты возьми и переведи сам что нибудь, а мы посмотрим, оценим. Человеку спасибо надо сказать за труд.