Collapse row layout to column at smaller widths without losing the margin-top for the previous element

Viewed 192

Targets

I am trying to accomplish the following:

  1. When the screen is enough wide, the .firstItem (red) and .lastItem (blue) elements, whose widths are unknown in advance, are on left and right part of parent .layout respectively.

  1. When the screen becomes narrower and horizontal spacing between items reduced to $minimalHorizontalSpaceBetweenItems, the layout collapses to column, but the .firstItem is still justified to left and .lastItem to right:

  1. The vertical space between items $verticalSpaceBetweenItems must not affect the layout in the single row configuration.

  2. The below CSS rule for the yellow .preceedingElement block above .firstItem and .lastItem must work:

.preceedingElement + .layout {
   margin-top: Npx;
}

Basic MWE

<div class="preceedingElement"></div>
<div class="layout">
  <div class="firstItem"></div>
  <div class="lastItem"></div>
</div>

SCSS:

.preceedingElement{
  width: 100%;
  height: 30px;
  background: #FAD7A0;
}

@mixin TwoSpacedItemsComposition(
  $minimalHorizontalSpaceBetweenItems: 0,
  $verticalSpaceBetweenItems: 0
) {
  
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  
  margin-top: -$verticalSpaceBetweenItems;
  margin-right: -$minimalHorizontalSpaceBetweenItems;
  
  
  > *{
    margin-top: $verticalSpaceBetweenItems;
    margin-right: $minimalHorizontalSpaceBetweenItems;
    
    &:last-child {
      margin-left: auto;
    }
  }
}
  
.layout {
  @include TwoSpacedItemsComposition(
    $minimalHorizontalSpaceBetweenItems: 12px,
    $verticalSpaceBetweenItems: 24px
  )
}

.firstItem {
  width: 200px;
  height: 34px;
  background: #EC7063;
}

.lastItem {
  width: 400px;
  height: 24px;
  background: #7FB3D5;
}

Flex solution attempt

Below SASS mixin implements all targets except fourth:

@mixin TwoSpacedItemsComposition(
  $minimalHorizontalSpaceBetweenItems: 0,
  $verticalSpaceBetweenItems: 0
) {
  
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  
  margin-top: -$verticalSpaceBetweenItems;
  margin-right: -$minimalHorizontalSpaceBetweenItems;
  
  
  > *{
    margin-top: $verticalSpaceBetweenItems;
    margin-right: $minimalHorizontalSpaceBetweenItems;
    
    &:last-child {
      margin-left: auto;
    }
  }
}

The interference is margin-top: -$verticalSpaceBetweenItems; for parent and margin-top: $verticalSpaceBetweenItems; for child.

JS Fiddle

Grid solution attempt

The grid always renders 2 rows.

@mixin TwoSpacedItemsComposition(
  $minimalHorizontalSpaceBetweenItems: 0,
  $verticalSpaceBetweenItems: 0
) {

  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, auto));
  grid-gap: $verticalSpaceBetweenItems $verticalSpaceBetweenItems;

  align-items: center;


  >* {

    &:first-child {
      justify-self: start;
    }

    &:last-child {
      justify-self: end;
    }
 }
2 Answers

Your flexbox approach seems to work perfectly... Changing margin-top: $verticalSpaceBetweenItems; to margin-bottom: $verticalSpaceBetweenItems; should solve requirement #4, and your original flexbox code solved the first three already.

JSFiddle: https://jsfiddle.net/yx689sh4/

Have you considered using percentages for the widths for your two items?

Related