Some Selected CSS Design Patterns

There are so many possible ways to set up and use CSS that a broad overview is not practical. Instead, I will show a few design patterns I like for accomplishing certain tasks.

Layering visual content

Something that tripped me up with html/css visuals at various points was creating layered content based off of different image, svg, and fill colors or gradients. Using absolute positioning to overlap elements was brittle and just a pain to deal with. I now have a couple techniques to deal with this that I like to use.

Use background: to layer content in a single element

The background: CSS parameter accepts more than one argument, assuming they are correctly specified. This example shows stacking a linear-gradient, a radial-gradient, an SVG drawing, and a PNG image. Note that the items will be displayed with precendence going to the later ones, so if the png takes up the hwhole space and has no opacity, it is the only thing that will be seen.
The background-positions can also be specified in a group, with each position being used for the corresponding number argument passed to background-image.

const backgroundFull = {
   backgroundImage: `
   linear-gradient(25deg, rgba(38,75,153,0.3) 25%, rgba(38,100,153,0.3) 25% 50% , rgba(38,125,153,0.3) 50% 70%,  rgba(38,150,153,0.3) 70% 90%),
   radial-gradient(circle at 40% 40%, rgba(31,118,77,0.8) 20%, rgba(150,150,150,0.3) 40% , rgba(190,190,190,0.3) 70%),
     url(/path/to/file.svg);
     url(/path/to/file.png);
   `,
   backgroundPosition: `
   left,
   right,
   50% 50%,
   25% 75%
   `
}

So this is one way to build a more complex visual by combining simpler ones in the background of an element. The downside is that all the backgrounds are in the same element and so partial overlap or offset is limited to what you can do in the space of that one element.

Use grid to overlap elements

I sometimes use this when I want a few things to occupy the same space to create a layered appearance. Grid can do complex overlapping by defining numerous rows/columns and placing items as desired, including with overlap. This allows for partial overlap, while also having separate content/backgrounds defined for those items.

const simpleGrid = { 
  display: 'grid',
  gridTemplateRows: '[top] 25% [mid] 50% [bottom] 25% [end]', 
  gridTemplateColumns: '[left] 30% [mid] 40% [right] 30% [end]', 
  gridTemplateAreas: `
    'lead     lead     lead '
    'leftbar pagemain pagemain '
    'leftbar pagemain pagemain '
    `,
}

// Place items such that the two items with exact row/column locations
// specified will overlap parts of the pagemain grid area.
  <div style={ simpleGrid } />
   <div style={{ gridArea: 'lead' }}
   <div style={{ gridArea: 'mid/mid/bottom/end' }}
   <div style={{ gridArea: 'pagemain' }}
   <div style={{ gridArea: 'leftbar' }}
   <div style={{ gridArea: 'bottom/right/end/end' }}
  </div>

Flex and Grid for setting up general layout

These two display: layouts set up the framework for pages effectively. Relative/Absolute positioning can then be used for detail work within this framework to make small adjustments.

When to use flex

Flexbox is very good at laying out items in columns or rows, one dimensionally. It can readily alter the layout in terms of spacing within the one dimension constraint. Both rows and columns can wrap if there is enough content.

Flex is not so good when:

If you want to work in two (or three) dimensions use grid. Grid handles rows and columns at the same time, and can also layer items on top of each other which flex is not designed for.

Setting up an element to use flex

The parent is set as flex, and the items inside will be arranged accordingly. Set the display: flex, the flexDirection at a minimum. alignItems and justifyContent are then two other major controls.
justify-content: - Primary alignment; positioning of elements on main axis. Will determine how elements are aligned left and right in a row or up and down in a column.
align-items: - Secondary alignment; affects alignment in other direction. So within a row up and down control and in a column left to right control.

const flexRow = { display: 'flex', flexDirection: 'row', alignItems: 'center'justifyContent: 'start'  }
const flexColumn = { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-around' }

Shifting items in flexbox

Use position: relative, top/left/right/bottom: 10px. The distance can be anything you want of course, but the strategy is just to switch position to relative and shift them. Any adjustments made this way will be from exactly where flexbox had put them, so you could fine tune the locations without losing the flex capabilities.

Overlapping in flexbox

If you want to overlap elements in flexbox without opening gaps, don’t use relative position changes. That method shifts the element without any concern for where the others are. Instead use negative margins and padding, which will shift items while re-aligning the others accoridingly.

// This supposes a column where you want the top level elements within
// the main flex colum to overlap for design purposes
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start'}} />
   <div>
      // initial element can stay right where flex places it   
   </div>
   <div style={{ margin:'-50px 0 0 0',zIndex: 12 }} > 
     <div style={{ padding:'50px 20px 50px 20px' }}  >
        // some nested content
     </div>
   </div>

   <div style={[ { margin: '-50px 0 0 0', zIndex: 11} ]} > 
     <div style={ { gridArea: 'main', width: '90%', padding:'50px 20px 50px 200px' } ]} >
       // more content
     </div>
   </div>
</div>

When to Use Grid

Grid adds functionality that flexbox did not have, but does not necessarily supercede it. It allows breaking a page up into row/column layout where you can place things based on that grid, including overlapping placements or empty space, all of which are things flex is not as good with.

Strengths of grid

Layouts with two or three dimensions, since it can do row/col, and also stack elements.

Weaknesses

If you don’t need more than one dimension it adds unneccesary complexity, and flex handles that arrangment better anyway, while being simpler to put in pace as well.

Defining the grid

You define rows and columns, and then each grid square is determined by the intersections of those rows/columns. The rows and columns need to be sized. Note that I usually make an [end] row/col as the last one with no size; this allows being able to specify the bottom/side of the grid so that items can be places right up to the edges. Otherwise, when specifying locations with the row/col syntax it always uses the start of the row or column, so you can’t actually go right up to the edge.

const gridDisp = { display: 'grid', 
  grid-template-columns: 10vh, 10vh, 60vh, 20vh, 
  grid-template-rows: 15%, 65%, 20%, 
  grid-template-areas: ` 
    "head head head"
    "sidebar subhead subhead"
    "sidebar content . "
    "sidebar content . "
   `
} 

Setting up the grid layout using grid-template-areas

Once rows and columns are set up, you can designate areas of the table by naming each grid square, with sqaures sharing a name being part of the same section. You don’t need to do this, but it can be a nice shortcut because otherwise you need to specify startrow/startcol/endrow/endcol. See above for an example.

Placing children on the grid

There are a couple ways, which either way you specify these locations on the child elements. So, while it is an option with flexbox to assign locations on the children, it is mandatory with grid.

Specifying start and end columns and rows

This is done in the css for each element. The start of both the start and end col/row is used. If you want to go to the end of a row/col, use the next one.

grid-column: startCol / endCol  
grid-row: startRow / endRow

Shorthand for start/end row/col

You can also specify them all in one shot with grid-area.

grid-area: rowstart / colstart / rowend / colend

Specifying an area created with grid-template-area

If you made grid-templates-areas, this is where it pays off. Locate items with one of the names you specified.

grid-area: name

Using CSS only dropdowns

I like this design pattern because it makes a pretty good looking and useful dropdown without having to do any JS work. Using transitions makes the appearance pretty smooth, and having it operate on both hover and click/touch means it will work on both mobile and other platforms. The specific version I give here is an Emotion/React component that takes a list of arguments that will become each one of the dropdown/link items.

const linkunformat = css`
  li a {
    text-decoration: none; 
    color: #dedede;
  }
`

const menuList = css`
  list-style: none;
  li {
    background-color: #222;
    line-height: 1.3rem; 
    color: #ddd;
  }
`

const ListFormat = (props) => {
  const height = props.height ? props.height : '30px'

  const menuContent = css`
    ul { 
      position: relative;
      margin: 0;
      padding: 0;
    }
    ul li {
      opacity: 0;
      height: 0px;
      transition: opacity 0.3s ease, height 0.3s ease;
    } 
    button + ul:active, button:focus + ul { 
      li {
        opacity: 0.95;
        height: ${height};
        transition: opacity 0.15s ease, height 0.3s ease;
      }
    };
    button:hover + ul li, ul:hover li { 
        opacity: 0.95;
        height: ${height};
        transition: opacity 0.15s ease, height 0.3s ease;
    };
  `

  return(
    <div 
      css={[ menuContent, {display: "flex", flexDirection: "column", justifyContent: 'center', flexWrap: "wrap", margin: '0.5vh 1vw 0.5vh 1vw', zIndex: 12 } ] }
    >
      <button css={[ { border: 'none', margin: '0', backgroundColor: '#00264f', color: '#e8e8e8', padding: '0.5rem', fontSize: '1.3rem', fontWeight: 400 } ]} >
         {props.title}
      </button>
      <ul css={[ linkunformat, menuList, { height: 0 } ]} >
      {props.mapvals.map( mapval => (
        <li key={mapval[0]} >
          <LinkTo 
            mapval={mapval} 
            linkstyle={props.linkstyle}
            linkhover={props.linkhover}
          />
        </li>
      ))}
      </ul>
    </div>
  )
}

const LinkTo = (props) => {
 const path = props.mapval[0]
 return(
   <Link to={path}
    css={[ hoverColor, taglink, props.linkhover, props.linkstyle, { display: 'block' } ]} 
   >
     {props.mapval[1]}
   </Link>
 )
}