Битва за ба­зо­вую ли­нию

Котики

Ко­гда-то наи­луч­шим ре­ше­ни­ем для ин­лай­но­вых бло­ков были, ну, ин­лайн-бло­ки. Мне они очень нра­вят­ся за то, что с их по­мо­щью мож­но ре­шить мно­же­ство за­дач. Но и они не все­мо­гу­щи. Они не уме­ют пра­виль­но ра­бо­тать с вер­ти­каль­ным вы­рав­ни­ва­ни­ем тек­ста по ба­зо­вой ли­нии шриф­та. При­чём про­бле­ма сле­ду­ет уже из спе­ци­фи­ка­ции (см. по­след­ний аб­зац):

  1. Для мно­го­строч­ных ин­лайн-бло­ков ба­зо­вой ли­ни­ей яв­ля­ет­ся ба­зо­вая ли­ния по­след­не­го строч­но­го бок­са в обыч­ном по­токе.

  2. Если внут­ри ин­лайн-бло­ка нет бок­сов обыч­но­го по­то­ка, или же у ин­лайн-бло­ка сто­ит over­flow от­лич­ный от vis­i­ble, то ба­зо­вой ли­ни­ей ста­но­вит­ся ниж­няя гра­ни­ца блока.

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

Вот при­мер: все три бло­ка име­ют dis­play: in­line-block. Пер­вый —обыч­ный од­но­строч­ный и с боль­шим пад­дин­гом, вто­рой —мно­го­строч­ный, но с мень­шим раз­ме­ром шриф­та, тре­тий —од­но­строч­ный, но с over­flow: auto.

Screenshot
I'm an inline-block
I'm an inline-block
With a second line
I'm an inline-block with an overflow auto

На этом при­ме­ре хо­ро­шо вид­но (Кста­ти, в по­след­них Са­фа­ри, вне­зап­но, блок с over­flow ве­дёт себя не по спе­ци­фи­ка­ции), где у ка­ко­го бло­ка на­хо­дит­ся ба­зо­вая линия.

in­line-table

В CSS было одно ме­сто, где вер­ти­каль­ное вы­рав­ни­ва­ние ра­бо­та­ло пра­виль­но —dis­play: in­line-table. За­ме­ня­ем ин­лайн-бло­ки на него и по­лу­ча­ем, ка­за­лось бы, то, что нужно:

Screenshot
I'm an inline-table
I'm an inline-table
With a second line
I'm an inline-table with an overflow auto

Но тут сра­зу вид­но: не ра­бо­та­ет over­flow: auto. К тому же та­ко­му бло­ку нуж­но за­да­вать table-lay­out: fixed. По­лу­ча­ет­ся иде­аль­но, если не ну­жен over­flow: auto.

Про­бу­ем флек­с­боксы

Мож­но ли сде­лать блок со скролл­ба­ром, ко­то­рый бу­дет пра­виль­но вы­рав­ни­вать­ся? Тут на по­мощь спе­шат флек­с­бок­сы, точ­нее, dis­play: in­line-flex. В тео­рии они та­к­же име­ют пра­виль­ное по­ло­же­ние ба­зо­вой ли­нии, но что мы по­лу­ча­ем на прак­тике?

Screenshot
I'm an inline-flex
I'm an inline-flex
With a second line
I'm an inline-flex with an overflow auto

Если вы по­смот­ри­те на этот при­мер в лю­бом бра­у­зе­ре кро­ме Fire­fox (да, даже в IE 10 и 12-й Опе­ре), то вы ви­ди­те идель­но вы­ров­нен­ные блоки.

Но в Fx блок с over­flow: auto, вне­зап­но, ра­бо­та­ет ана­ло­гич­но ин­лайн-бло­ку: те­ря­ет ба­зо­вую ли­нию. Грусть, пе­чаль, разо­ча­ро­ва­ние, ждём ис­прав­ле­ния све­же­за­ре­пор­чен­но­го бага.

А если иначе?

Очень здо­ро­во, что in­line-flex сам по себе пра­виль­но вы­рав­ни­ва­ет­ся от­но­си­тель­но осталь­ных бло­ков, и, если бы не баг Fx, всё было бы со­всем за­ме­ча­тель­но. Но что если мы по­про­бу­ем вы­рав­ни­вать не раз­ные in­line-flex от­но­си­тель­но друг дру­га, а эле­мен­ты внут­ри флек­с­бокса?

Screenshot
I'm an inline-flex
I'm an inline-flex
With a second line
I'm an inline-flex with an overflow auto

Оп! Всё ра­бо­та­ет! Вот толь­ко… Если от­дель­ные бло­ки с in­line-flex сами по себе пе­ре­но­сят­ся на но­вую стро­ку, то для эле­мен­тов внут­ри флек­с­бок­са нам надо было бы при­ме­нять flex-wrap. Но Fire­fox его не под­дер­жи­вал до вер­сии 28.0.

Всё вместе

Так! Но ведь если in­line-flex про­ки­ды­ва­ет свою ба­зо­вую ли­нию на­верх, а внут­рен­ний блок с over­flow: auto та­к­же име­ет пра­виль­ное вы­рав­ни­ва­ние даже в Fire­fox, то мож­но же сов­ме­стить! До­ба­вим в каж­дый блок по до­пол­ни­тель­но­му эле­мен­ту, да бу­дем за­да­вать пад­дин­ги и over­flow уже на них:

Screenshot
I'm an inline-flex
I'm an inline-flex
With a second line
I'm an inline-flex with an overflow auto

В нор­маль­ных бра­у­зе­рах ни­че­го не по­ме­ня­лось, те­перь по­смот­рим на Fire­fox… Так, блок уже вы­рав­ни­ва­ет­ся не по ниж­ней гра­ни­це, но и не по ба­зо­вой ли­нии. Хотя, из­ме­рим раз­ни­цу: 10 пик­се­лей. Это же наш пад­динг! Уби­ра­ем пад­дин­ги по оче­ре­ди —всё вы­рав­ни­ва­ет­ся пра­виль­но, ко­гда верх­ний пад­динг ста­но­вит­ся ра­вен нулю. Ага, зна­чит Fx в этом слу­чае всё де­ла­ет по­чти вер­но, вот толь­ко всплыл но­вый баг. Пока мы ждём его ис­прав­ле­ния, из­ба­вим­ся-ка от пад­дин­га, за­ме­нив его на псев­до-элемент:

Screenshot
I'm an inline-flex
I'm an inline-flex
With a second line
I'm an inline-flex with an overflow auto

Иде­ально!

Послед­ние штрихи

Ну лад­но, не иде­аль­но. Оста­ёт­ся пара ме­ло­чей, ко­то­рые мо­гут про­явить­ся в де­ся­том IE и в две­на­дца­той Опере.

В IE при за­дан­ной ши­рине флек­с­бок­са текст внут­ри него не бу­дет вра­пать­ся, даже если не бу­дет сто­ять white-space: nowrap. До­воль­но стран­ный баг, об­хо­дит­ся либо до­бав­ле­ни­ем внут­рен­не­му бло­ку яв­ной ши­ри­ны в 100%, либо, что пра­виль­нее, -ms-flex-neg­a­tive: 1.

В Опе­ре очень по­хо­жий баг —внут­рен­ний блок не ре­а­ги­ру­ет на за­дан­ную ши­ри­ну, из-за чего в бло­ке нет пе­ре­но­сов. Един­ствен­ный спо­соб это ис­пра­вить, ко­то­рый я на­шёл: до­ба­вить ро­ди­те­лю flex-di­rec­tion: col­umn —так как у нас все­гда толь­ко один внут­рен­ний эле­мент, это ни на что не по­вли­яет.

Те­перь иде­аль­но (Раз­ве что мож­но ещё до­ба­вить фол­бе­ки для ста­рых бра­у­зе­ров, но это уже вы­хо­дит за рам­ки этой ста­тьи). Вот по­след­ний при­мер с раз­ны­ми ва­ри­ан­та­ми, ко­то­рые пе­ре­но­сят­ся со строч­ки на строчку:

Screenshot
Just some inline text
I'm an inline-flex
I'm an inline-flex
With a second line
I'm an inline-flex with an overflow auto
I'm an inline-flex with the text wrapped on the next lines
I'm just another inline-flex block with a lot of content and overflow: auto.

Код по­лу­ча­ет­ся таким:

.flex {
    dis­play: -ms-in­line-flexbox;
    dis­play: -we­bkit-in­line-flex;
    dis­play: in­line-flex;

    /* Fix­ing Opera is­sue */
    flex-di­rec­tion: col­umn;

    ver­ti­cal-align: base­line;
    }

.flex-con­tent {
    padding: 0 10px 10px;
    bor­der: 1px solid lime;

    /* Fix­ing IE is­sue */
    -ms-flex-neg­a­tive: 1;
    }

/* Fix­ing Fx is­sue */
.flex-con­tent:be­fore {
    con­tent: "";
    dis­play: block;
    padding-top: 10px;
    }

Итого

Ох уж этот Fire­fox! Если бы не его баги (и один баг де­ся­то­го IE), то мы мог­ли бы обой­тись од­ним эле­мен­том для каж­до­го ин­лай­но­во­го бло­ка, ко­то­рый мы хо­тим вы­ров­нять по ба­зо­вой ли­нии. А если вам не ну­жен over­flow от­лич­ный от vis­i­ble, и вы не бо­и­тесь таб­лиц, то мож­но по­про­бо­вать ис­поль­зо­вать dis­play: in­line-table.

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