Skip to content

Instantly share code, notes, and snippets.

@RussMax783
Last active August 18, 2017 21:52
Show Gist options
  • Save RussMax783/916cf9a0a5e85446666751b85c2f915b to your computer and use it in GitHub Desktop.
Save RussMax783/916cf9a0a5e85446666751b85c2f915b to your computer and use it in GitHub Desktop.
basecomponent-expansion.md

I want to create a way that is easy to define the layout of components in the markup so that it will work on Web and React Native. Even if we don't go down the React Native route there is still so much to learn from how they structured their APIs and how they implemented certain aspects of how you interact with the DOM. I also think it would be super cool for Jane to have an open source project that works on React and React Native that is based on our base components. Utilizing styled components and themes we can do some really cool things. We can do a lot of what Leland Richardson talked about and create our components in such a way that they are easily shareable between web and native devices. To accomplish this with what we have currently built in Jane components, we need a couple more things, and i want to get your feed back on these components API/prop types.

Space

The space component is used to add gutter in-between items. it only takes and x, y, top, bottom, left, and right. The current gutter prop will only work on web and if we consider React Native we need an easy way to define spacing. Here is a couple options:

Specifically add space whenever needed

Using a component that looks like this:

<Space y={14} /> // has a height of 14
<Space x={14} /> // has a width of 14
<Space top={14} /> // has a margin-top of 14
<Space bottom={14} /> // has a margin-bottom of 14
<Space left={14} right={14} /> // has a margin-left and margin-right of 14

instead of this (which is what we currently have

<Col gutter={16} paddingBottom={24} paddingLeft={16}>
	<Col gutter={4}>
		<Text fontSize={16} color="gray20" weight="300">Title</Text>
		<Text fontSize={14} color="gray40" weight="300">Description</Text>
	</Col>
	<Text>some other text</Text>
</Col>

we would change a little bit and do this

<Col paddingBottom={24} paddingLeft={16}>
	<Col>
		<Text fontSize={16} color="gray20" weight="300">Title</Text>
		<Space y={4} />
		<Text fontSize={14} color="gray40" weight="300">Description</Text>
	</Col>
	<Space y={16} />
	<Text>some other text</Text>
</Col>

In the cases where we map over an array and create a list of elements which usually looks like this:

{someList.map((x) => (
    <Item ... />
))}

we do a tiny bit extra and do

{someList.map((x, idx) => (
  <Space top={idx ? 12 : 0} key={item.id}>
    <Item ... />
  </Space>
))}

this will wrap the child and add margin top of 12 on all elements except the first one, spacing out all the elements with 12

I lean towards this way as there are instances where a design has spacing between elements that varies more then just 12px between each item. This easily allows us to explicitly add margins to space things as needed and if the spacing changes between responsive points, we simply wrap the space in a media components.

Iteration

We could keep Web the same and let css do its thing with selectors, but In React Native we map over children add add<Space y={gutter} /> in between items. As long as our layouts are pure I don't see this being a performance issue. This would be somewhat similar to what react native does to create lists, such as List and FlatList

Media Component

we can create a simple component that will choose to render its children based of two simple props, from and to.

<Media to="small">
  ... content rendered up to small
</Media>

<Media from="small" to="large">
  ... content rendered from small breakpoint to large
</Media>

<Media from="medium">
  ... content rendered from medium breakpoint and up
</Media>

The most often thing i've seen when needing something to be responsive is simply switching between flex-directions at a certain point.

const Header = styled.div`
  display: flex;
  flex-direction: column;
  ${({ theme }) => theme.media.smallMin`
    flex-direction: row;
    justify-content: center;
  `}
`

We can abstract this ab it and create a RowCol components or a ColRow. This will take a breakpoint and switch between a Row or Col at the breakpoint respectively. It also will take responsiveProps which will be an array of two objects that will be put onto the respective Row or Col.

This will render a row up too small and from small up, it will be a Col with justifyContent: 'center'

<RowCol {/* or ColRow */}
  breakpoint="small" 
  responsiveProps={[
   { },  
   { justifyContent: 'center' }  
  ]}
/>

This will render a row up too small with justifyContent: 'center' but from small up, it will be a Col and no longer be centered

<RowCol {/* or ColRow */}
  breakpoint="small" 
  responsiveProps={[
   { justifyContent: 'center' },  
   { }  
  ]}
/>

This will render a row up too medium with justifyContent: 'center' and from small up, it will be a Col and will still have justify center

<RowCol {/* or ColRow */}
  breakpoint="medium"
  justifyContent="center"
/>

Touchable

we'll need a touchable that willl basically standardize the onClick vs the onPress between React and React Natve

Link

This one will be tricky. How do we route within the app where web is url driven and native is stack driven... One option i've thought about is based on the navigators we use where ReactRouter is used on web and has a Link, and on native would probably be ReactNavigation, is to create an abstraction of the two and route based on the device we are on.

@megamaddu
Copy link

Space

I like this:

  <Space all={5} vertical={2} top={20}>
    <Item ... />
  </Space>

Would there ever be a need for child-less space, like in your grid example? Or would you just wrap the Col in <Space bottom={16}>...Col...</Space>?

Media

Looks great. Are to and from both inclusive?

RowCol

Meh.. just:

<Media to="medium">
  <Col ...>
</Media>
<Media from="large">
  <Row ...>
</Media>

Touchable

Yes. Or maybe just Touch. Or Interaction. Either way, every user interaction event should come from one of these.

Link

Dunno enough about native. Just style a RR Link?

@RussMax783
Copy link
Author

Would there ever be a need for child-less space

I think it would probably be cleaner markup with something like

<Text>Some text</Text>
<Space vertical={14} />
<Text>Some other Text</Text>

vs

<Space button={14}>
  <Text>Some text</Text>
</Space>
<Text>Some other Text</Text>

But you could actually do both ways

@RussMax783
Copy link
Author

I think the reason why you would want to do that second way is to not interrupt the flow of the parent flex, You wouldn't want to wrap content in a Space component that should still be fitting in somewhere, according to its parent rules, such as

<Col justify="center">
  <Text>...</Text>
  <Text>...</Text>
  <Text>...</Text>
</Col>
<Col justify="center">
  <Space vertical={14}>
    <Text>...</Text>
    <Text>...</Text>
  </Space>
  <Text>...</Text>
</Col>

But i think space should never have more then one child.

@RussMax783
Copy link
Author

Are to and from both inclusive?

Not sure what you mean here

@RussMax783
Copy link
Author

Also the thing about RowCol is that sometimes there is a lot of content inside a Row or Col. You could do what you said, but then we may also have a ton of duplicated code for the children.

@zacanger
Copy link

zacanger commented Aug 17, 2017

I like Space.

A little unsure about RowCol; I definitely see how it could make things more convenient, but I think it would be really difficult to remember the API.

I like Media but I'm not sure there's a huge need there, especially since I don't think it negates the need for media queries and styles, and with a small helper they're not annoying:

const Foo = styled(Thing)`
  ${media.largest`
    font-size: 1.2 em;
  `}
`

But it also definitely wouldn't hurt to have it.

Touchable comes back to Trotter's original rewrite of jane-components: is it necessary? If we're definitely going to be using React Native then yeah, it is. I don't love the idea of adding extra calls if that's not the case, because it's just more code and more stuff to think about, but if the goal is to build Seller in RN, I think it's needed.

Link is complicated, especially since we also have to think about actual links (as in, a href under the hood) for going outside the app. I don't know what to do about that, but I'd like to avoid any more weird hacks like what's going on in jane-com's Link.

@megamaddu
Copy link

megamaddu commented Aug 17, 2017

On Touchable,

because it's just more code and more stuff to think about
One of the reasons I like it is that it ends up being less to think about, "should this base component support clicking? hovering? dragging?" and "does this component I'm using support clicking? etc" rather than "ok, I need to click or drag this thing, pull in the interactable wrapper".

Media and CSS media queries are similar, but they do have differences. CSS media is good for toggling a couple styles on a single component, where that component should always be rendered and needs slight adjustments for different screens. Media is good for altering the DOM tree based on screen size, when entire sections are not displayed or multiple siblings will be affected by the screen size. CSS media queries can still achieve most of that from the user's perspective, but it's more rendering work and might cause more data fetching than necessary.

Are to and from both inclusive?

If I have these components

<Media to="medium">...</>
<Media from="medium">...</>

do they both render when the current size is medium?

@megamaddu
Copy link

megamaddu commented Aug 17, 2017

And I do like this for some cases:

<Text>Some text</Text>
<Space vertical={14} />
<Text>Some other Text</Text>

Would vertical always be 14px in this case? Half above and half below? Or would Space behave differently when there are children. It might be easier to make the component if we have one for "being" space and one for "adding" space.
Space and Pad maybe.

The other example usage I'm comparing to:

<Space all={5} vertical={2} top={20}>
  <Item ... />
</Space>

@RussMax783
Copy link
Author

To is exclusive and from is inclusive. Because we want to display something "up to medium" at that point when we hit medium we are passed the medium breakpoint. so for a medium breakpoint, from would be from the medium break point and on. Hence From would be inclusive. Could change to to be something like uptoor something more specific?

@RussMax783
Copy link
Author

Trotter, i like your idea of Space and Pad. The Api becomes a little more clear. Space is strictly horizontal or vertical space, and Pad takes children and will add margin to any direction. I think it looks pretty clean too

<Row>
  <Pad after={12}><Text fontSize={12}>1...</Text></Pad>
  <Pad after={12}><Text fontSize={12}>2...</Text></Pad>
  <Text fontSize={12}>3...</Text>
</Row>

or

<Row>
  <Text fontSize={12}>1...</Text>
  <Space horizontal={12} />
  <Text fontSize={12}>2...</Text>
  <Space horizontal={12} />
  <Text fontSize={12}>3...</Text>
</Row>

With this, yes there are more dom nodes, but remember that components try and keep themselves internalized. So we should be using the padding on a Row or Col more the space. Space should be used more for layout only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment