Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Creating API for components: flexbox layout
Alexander Vechy
Alexander Vechy

Posted on

     

Creating API for components: flexbox layout

How many times did you writedisplay: flex? This goes so common thatsome people tried applyingdisplay: flex to almost all elements on the page.

In this post we will go through the thoughts process behind the API decisions for the most used component.

I've been thinking to write this for a while as I continue to see different implementations of aflexbox component, each with own API. I think we should stop inventing and standardize this.

New standards is born in an attempt to unify the standards

xkcd: Standards (https://xkcd.com/927/)

Start

In this article I'm going to use React andstitches (I amstill in love with it). But the main idea of this article is to justify the API decisions that can be then applied in Vue, Svelte, Lit, or any other front-end tool.

Let's start simple:

import{styled}from'@stitches/react'exportconstFlex=styled('div',{display:'flex',})
Enter fullscreen modeExit fullscreen mode

For the sake of simplicity, I'm using pre-configuredstyled directly fromstitches, but I in your libraries I encourage to usetheme tokens for consistent layout properties, colors, font sizes, etc.

Wrapping

Let's start simple and addflex-wrap control:

import{styled}from'@stitches/react'exportconstFlex=styled('div',{display:'flex',variants:{wrap:{'wrap':{flexWrap:'wrap',},'no-wrap':{flexWrap:'nowrap',},'wrap-reverse':{flexWrap:'wrap-reverse',},},},})
Enter fullscreen modeExit fullscreen mode

I'm usingstitches variants that produce nice TypeScript props forFlex component

That was the simplest API decision to make, we only removedflex word to avoid repetitiveness, because all props exist in the context ofFlex element already. Bear in mind, that the default browser value isnowrap, so using<Flex wrap="wrap"> can be a common thing. Although it might feel weird, it's still easier to learn and use (likeflex-wrap: wrap), comparing to a made-up API.

Flow direction

Let's move on to the second prop:flex-direction.
I've seendirection used in some Design Systems, but for some people (me) it can be even worse than writingcosnt, especially because it's a commonly used prop.
Other Design Systems incorporateRow andColumn components – they provide great context for the consumer:

// Flex defined as beforeexportconstRow=styled(Flex,{flexDirection:'row',})exportconstColumn=styled(Flex,{flexDirection:'column'})
Enter fullscreen modeExit fullscreen mode

Although now we also need to handle the cases when we want to useflex-direction: row-reverse; // or column-reverse. So, we either addreverse boolean prop (since it's not a common prop to be used):

// Flex defined as beforeexportconstRow=styled(Flex,{flexDirection:'row',variants:{reverse:{true:{flexDirection:'row-reverse'}}}})exportconstColumn=styled(Flex,{flexDirection:'column',variants:{reverse:{true:{// neat way to create boolean variants in stitchesflexDirection:'column-reverse'}}}})
Enter fullscreen modeExit fullscreen mode

... or we define flow direction directly in theFlex component:

exportconstFlex=styled('div',{display:'flex',variants:{wrap:{},// defined as beforeflow:{'row':{flexDirection:'row',},'column':{flexDirection:'column',},'row-reverse':{flexDirection:'row-reverse',},'column-reverse':{flexDirection:'column-reverse',},},},})
Enter fullscreen modeExit fullscreen mode

As youmight know,flex-flow is a shorthand forflex-direction andflex-wrap, so we're not making up the API again, but adopting it.

The usage so far would be (overriding browser defaults):

<Flexflow="row-reverse"wrap="wrap"/><Flexflow="column"/>// or with dedicated components<Rowreversewrap="wrap"/><Column/>
Enter fullscreen modeExit fullscreen mode

Which API you like the most is up to you, both of them work great. I would prefer having justFlex or having all 3 of them.Flex itself is easy to maintain and it provides enough context straight away looking atflow prop, especially when it needs to change based on screen size, usingresponse styles:

<Flexflow={{'@tablet':'row','@mobile':'column'}}/>
Enter fullscreen modeExit fullscreen mode

Imagine doing this with dedicatedRow andColumn components.

Alignment

So, making quite a good progress here, let's move on to the most interesting part: alignments.
While the official API for this would be to usejustify-content andalign-items, I always thought that both of these words make little sense to me when writing CSS. Maybe it's because I'm not a native English speaker, or maybe they don't make much sense when thinking about flex boxes.

One of the greatest articles that helped me to understand these properties wasA Complete Guide to Flexbox (most of us still referring to). It has awesome visualizations that show how these properties affect items positions by the change of what is calledmain axis andcross axis. What really helped me though, wasflutter'sFlex widget. It has these two awesome attributes:mainAxisAlignment andcrossAxisAlignment and the usage is:

Flex(mainAxisAlignment:MainAxisAlignment.spaceEvenly,crossAxisAlignment:CrossAxisAlignment.end,)
Enter fullscreen modeExit fullscreen mode

What's really great about this API, is that it's really easy to visualize in your head. If you have arow, your main axis is horizontal, if you have acolumn, it is vertical. So, no matter the direction, you can imagine your items evenly spaced on the main axis and aligned to the end of the container on the cross axis.

flex main axises for row and column

CSS-tricks: Directions (https://css-tricks.com/snippets/css/a-guide-to-flexbox/#flex-direction)

Knowing this, we can incorporate new API into our own component:

exportconstFlex=styled('div',{display:'flex',variants:{wrap:{},flow:{},main:{'start':{justifyContent:'flex-start',},'center':{justifyContent:'center',},'end':{justifyContent:'flex-end',},'stretch':{justifyContent:'stretch',},'space-between':{justifyContent:'space-between',},},cross:{start:{alignItems:'flex-start',},center:{alignItems:'center',},end:{alignItems:'flex-end',},stretch:{alignItems:'stretch',},},},})
Enter fullscreen modeExit fullscreen mode

Comparing toflutter'sFlex API, I shortenedmainAxisAlignment tomain andcrossAxisAlignment tocross. While TypeScript provides great autocomplete experience, seeing these long property names when composing multipleFlex components was hurting my eyes. Since both of the properties exist in the context ofFlex component, I believe it's enough to understand them.

Now, the usage would be:

<Flexflow="column"main="space-between"cross="center"/>
Enter fullscreen modeExit fullscreen mode

flex-column example

The thought process for this component is fairly easy (or the one you can get used to): it's a column, so items will be evenly distributed across the main axis (y), and across axisx they will be centred.

By the way,new Chrome Dev Tools flexbox visual debugging is awesome.

Spacing

Now, the last prop we need to add is the one that controls spacing between the child elements. There were two approaches, generally:no-side-effects-but-nested-divs-one which wraps every children into box with negative margins to allow proper wrapping behaviour without changing the child nodes styles, andflex-gap-polyfill one, that changes the styles of its children through> * selector. Gladly, we don't need to talk about them today, since Safary 14.1 was the last one among the big folks to bereleased with the support offlexbox gap property. Thankfully, Apple is quite pushing in regards to updates, so we can seeglobal browser support growing pretty fast.

exportconstFlex=styled('div',{display:'flex',variants:{// the rest of the variantsgap:{none:{gap:0,},sm:{gap:'4px',},md:{gap:'8px',},lg:{gap:'16px',},},},})
Enter fullscreen modeExit fullscreen mode

Few things to comment here. First, you can still use pollyfilled option here, seean example from an awesomeJoe Bell. Second, usexs,sm, etc tokens only if they are already incorporated in your Design System, otherwise, you may considerTailwindCSS number-tokens instead. Third, we don't implement powerfulrow-gap andcolumn-gap CSS properties, but you can do them same way as for thegap. Third, we keep'none' option to be able to setgap conditionally in a clear way, for example through@media breakpoints:gap={{ '@desktop': 'none', '@tablet': 'lg' }}.

End

And that's it! I really hope that more and more people would start seeing their UIs as a composition of layout and interactive elements, writing very little of CSS.

You can see some usage exampleshere. As with many things, you get the taste in the process, so feel free to play around with the playgrounds, see how such props help your intuition with visualizing the items.

Full example
import{stlyed}from'@stitches/react'exportconstFlex=styled('div',{display:'flex',variants:{wrap:{'wrap':{flexWrap:'wrap',},'no-wrap':{flexWrap:'nowrap',},'wrap-reverse':{flexWrap:'wrap-reverse',},},flow:{'row':{flexDirection:'row',},'column':{flexDirection:'column',},'row-reverse':{flexDirection:'row-reverse',},'column-reverse':{flexDirection:'column-reverse',},},main:{'start':{justifyContent:'flex-start',},'center':{justifyContent:'center',},'end':{justifyContent:'flex-end',},'stretch':{justifyContent:'stretch',},'space-between':{justifyContent:'space-between',},},cross:{start:{alignItems:'flex-start',},center:{alignItems:'center',},end:{alignItems:'flex-end',},stretch:{alignItems:'stretch',},},gap:{none:{gap:0,},sm:{gap:'4px',},md:{gap:'8px',},lg:{gap:'16px',},},display:{flex:{display:'flex',},inline:{display:'inline-flex',},},},})
Enter fullscreen modeExit fullscreen mode

Key takeaways:

  • keep the API as close to the official specs as possible, making it easy to learn
  • make up own API is possible, but maybe there's some solution that is fairly common and people are used to it
  • learning other tools, likeFlutter can open new perspectives

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software Engineer in love with UI Engineering
  • Location
    Barcelona, Spain
  • Education
    Master's degree in Software Engineering
  • Work
    Software Engineer
  • Joined

More fromAlexander Vechy

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp