npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

styled-layout

v0.5.0

Published

Simple responsive layout components built with React and styled-components

Downloads

15

Readme

Motivation

The problem

There are many approaches to handling layout in todays user interfaces on the Web. We have come from using floats and clearfixes to flexbox and grids in CSS to achieve fluid and flexible layouts that are responsive across various device sizes.

The rise of design systems has brought forth the shortcomings of the layout approaches we have been using so far. For example the most common way to create white space between UI components is to attach margins directly to components via class names. This approach is far from perfect and has multiple issues:

  1. Changing the position of components is hard since it requires moving the attached margin to another component
  2. How do you decide which component should have the spacing attached to it? (margin-left or margin-right?)
  3. It's impossible to know what the spacing is just by looking at the render method - instead you have to peak in the CSS level to know where the margins are attached.
  4. Components are too context-aware and thus not properly reusable

The solution

When you shift your thinking away from attaching spacing directly to components to treating layout and spacing inside your UI as components themselves you open the door for a whole new world of layout management. By using layout components, such as Stack or Spacer, your move the higher level responsibility of handling the layout to a separate component while still being able to have lower level control over the spacing between components. In this way you are able to design your other components in a reusable manner without having to think about the context they appear in and the white space around them.

Inspirational resources

Getting started

Installation

First, install the library.

npm install styled-layout

or with Yarn:

yarn add styled-layout

Prerequisites

In order to use the layout components we first need to add spacing units/tokens to the theme. The tokens need to be added inside an object called spacing and there needs to be at least one spacing unit called default but other than that the units can be named based on any naming convention you are most comfortable with.

If you are not familiar with styled-components theme setup with TypeScript you can read about it here.

import { DefaultTheme } from 'styled-components';

export const theme: DefaultTheme = {
  spacing: {
    none: '0px',
    xxsmall: '2px',
    xsmall: '4px',
    small: '8px',
    normal: '16px',
    default: '16px',
    medium: '24px',
    large: '32px',
    xlarge: '48px',
    xxlarge: '64px',
  },
  // ... other theme values ...
};
import { ThemeProvider } from 'styled-components';
import { theme } from './theme';

const App = () => {
  <ThemeProvider theme={theme}>{/* Components */}</ThemeProvider>;
};

Basic usage

import React from 'react';
import styled from 'styled-components';
import { Stack, Spacer } from 'styled-layout';

const Component = () => (
  <Stack>
    <p>Basic stack</p>
    <div>Item 1</div>
    <div>Item 2</div>

    <Spacer size="large" />

    <Stack axis="x" spacing="small">
      <div>Item 3</div>
      <div>Item 4</div>
    </Stack>
  </Stack>
)

Check the available props for each layout component in the Components section.

You can also take a look at the example folder for more comprehensive usage of the layout components.

Dividers

Dividers allow you to create visually more distinct separation between elements inside a stack. You can add dividers to a Stack via the dividers prop that, in it's most basic form, accepts a boolean to toggle the dividers on or off.

import React from 'react';
import styled from 'styled-components';
import { Stack } from 'styled-layout';

const Component = () => (
  <Stack dividers>
    <div>Item 1</div>
    <div>Item 2</div>
    <div>Item 3</div>
    <div>Item 4</div>
  </Stack>
);

You can control the divider color by defining a color called divider in your theme under colors (otherwise the default color for the divider line is #ddd). However, it is quite common that you need more color options for your dividers which is why you can easily use any color by adding more colors to your theme and referencing them in the divider's color prop.

// theme.ts
import { DefaultTheme } from 'styled-components';

export const theme: DefaultTheme = {
  colors: {
    primary: 'tomato',
    divider: '#eee', // default color for all dividers
    'grey-10': '#f5f5f5',
    'grey-30': '#eeeeee',
    'grey-50': '#dddddd',
    'grey-70': '#888888',
    'grey-90': '#444444',
    // ... other colors ...
  },
};

// component.tsx
import React from 'react';
import { Stack } from 'styled-layout';

const Component = () => (
  <>
    <Stack dividers="grey-90">
      <div>Item 1</div>
      <div>Item 2</div>
    </Stack>

    <Stack dividers="primary">
      <div>Item 3</div>
      <div>Item 4</div>
    </Stack>
  </>
);

In certain cases you might need a more fined grained control over the dividers inside a stack. If you render a Divider component inside a Stack component that divider will overwrite the default divider that would otherwise appear in it's place.

import React from 'react';
import styled from 'styled-components';
import { Stack, Divider } from 'styled-layout';

const Component = () => (
  <Stack spacing="normal" dividers>
    <div>Item 1</div>
    <div>Item 2</div>
    <Divider size="large" />
    <div>Item 3</div>
    <div>Item 4</div>
  </Stack>
);

Media queries

Media queries are commonly used to create responsive styles for components. In most cases you don't need to write media queries by yourself when using styled-layout but instead you can utilize a more ergonomic way of defining responsive styles: responsive props. You might have seen these kind of responsive props in the wild where the props are passed as an array, eg. in styled-system. Instead of using the array syntax for responsive props styled-layout uses an alternative object syntax.

Start by defining the breakpoints that are part of your design system. The name of the each breakpoint is totally up to you to decide - they can be eg. phone | tablet | desktop | monitor or if you fancy more Bootstrap like names sm | md | lg | xl.

const breakpoints = {
  phone: { min: 0, max: 767 },
  tablet: { min: 768, max: 1023 },
  desktop: { min: 1024, max: 1279 },
  monitor: { min: 1280, max: Infinity },
};

You can optionally add Up/Down variants for your breakpoints by omiting the other min/max value.

const baseBreakpoints = {
  phone: { min: 0, max: 767 },
  tablet: { min: 768, max: 1023 },
  desktop: { min: 1024, max: 1279 },
  monitor: { min: 1280, max: Infinity },
};

const breakpoints = {
  ...baseBreakpoints,
  tabletDown: { max: baseBreakpoints.tablet.max },
  tabletUp: { min: baseBreakpoints.tablet.min },
  desktopDown: { max: baseBreakpoints.desktop.max },
  desktopUp: { min: baseBreakpoints.desktop.min },
};

Finally, create a media utility and add it to your theme. Note that we are also exporting the media helper here since it's quite a handy tool to use in your custom styled components too.

import { DefaultTheme } from 'styled-components';
import { createMediaQuery } from 'styled-layout';

export const media = createMediaQuery(breakpoints);

export const theme: DefaultTheme = {
  breakpoints,
  media,
  // ...other theme values ...
};

This will enable responsive props for all components in styled-layout. The default value in the responsive prop object is represented by _ key and the other fields come from the breakpoints that were added to the theme.

import { Stack, Spacer } from 'styled-layout';

<Stack axis={{ _: 'x', phone: 'y' }}>
  <h1>Responsive props</h1>
  <Spacer size={{ _: 'large', tablet: 'normal' }} />
  <p>No need to add your own media queries 🎉</p>
</Stack>;

We can also the media helper that we exported earlier in any styled component.

import styled from 'styled-components';
import { media } from './theme';

const CustomComponent = styled.div`
  padding: ${p => p.theme.spacing.large};

  ${media.phone`
    padding: ${p => p.theme.spacing.normal};
  `}
`;

You might be asking why we are using the exported media helper instead of the one we put in the theme.

That's a good question.

For some reason CSS syntax highlighting only works when we are directly using the media helper. So, by using ${media.phone} instead of ${p => p.theme.media.phone} the CSS is highlighted correctly (at least in VSCode). However, the CSS autocompletion doesn't seem to work in either case 😅

Components

Stack

| Prop | Type | Default | Note | | --------- | ------------- | ------------ | ----------------------------------------------- | | axis | 'x' / 'y' | 'y' | | | spacing | string | 'default' | Based on spacing tokens in theme | | fluid | boolean | false | Determines whether the stack items should wrap. | | align | string | flex-start | Use any flexbox align-items value. | | justify | string | flex-start | Use any flexbox justify-content value. |

Spacer

| Prop | Type | Default | Note | | ------ | ------------- | ----------- | -------------------------------- | | axis | 'x' / 'y' | 'y' | | | size | string | 'default' | Based on spacing tokens in theme |

Divider

| Prop | Type | Default | Note | | ------- | -------- | ----------- | --------------------------------------------------------- | | size | string | 'default' | Based on spacing tokens in theme. | | color | string | 'divider' | Based on color tokens in theme (or #ddd without theme). |

Utilities

createMediaQuery

interface BreakpointRange {
  min: number;
  max: number;
}

type Breakpoints = {
  [breakpoint: string]:
    | BreakpointRange
    // Up variant
    | Omit<BreakpointRange, 'min'>
    // Down variant
    | Omit<BreakpointRange, 'max'>;
};

const breakpoints: Breakpoints = {
  /* Add your breakpoints */
};

const media = createMediaQuery(breakpoints);

License

MIT.