Skip to content

martinsbicudo/styled-container-queries

Repository files navigation

styled-container-queries

GitHub Workflow Status (with event) minified size npm

Simple lib to use container queries with styled-components


Inspiration

Inspired by styled-breakpoints

Compatibility

Bundle Analyzer

Quick Menu

How to use

Install

npm i styled-container-queries

#or

yarn add styled-container-queries

#or

pnpm add styled-container-queries

Simple example

theme.ts

import { createStyledContainerQueries } from "styled-container-queries";

const breakpoints = {
  sm: "500px",
  md: "700px",
  lg: "900px",
} as const;

const containerTheme = createStyledContainerQueries(breakpoints);

const theme = {
  ...containerTheme,
  ...styledTheme,
};

export { theme };

styled.ts

import styled from "styled-components";

export const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("sm")} {
    & > p {
      background-color: red;
    }
  }

  ${({ theme }) => theme.container.inline.down("sm")} {
    & > p {
      background-color: yellow;
    }
  }
`;

styled.d.ts

This is the current way to solve types

import "styled-components";
import { theme } from "./theme";

declare module "styled-components" {
  export interface DefaultTheme {
    container: typeof theme.container;
  }
}

main.tsx

import { ThemeProvider } from "styled-components";
import { theme } from "./theme";
import * as S from "./styled.ts";

const Main = () => (
  <ThemeProvider theme={theme}>
    <S.Container>
      <p>example text</p>
    </S.Container>
  </ThemeProvider>
);

export { Main };

Documentation

Theme structure

Create theme

import { createStyledContainerQueries } from "styled-container-queries";

const breakpoints = {
  sm: "200px",
} as const;

const containerTheme = createStyledContainerQueries(breakpoints);
const containerTheme = {
  //return query and container-type: inline-size
  inline: {
    up,
    down,
    only,
    between,
    attrs,
  },
  //return query and container-type: size
  size: {
    up,
    down,
    only,
    between,
    attrs,
  },
  //return query and container-type: normal
  normal: {
    up,
    down,
    only,
    between,
    attrs,
  },
  //return only query without `container-type`
  query: {
    up,
    down,
    only,
    between,
    attrs,
  },
};

Container types

Inline Size

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md")} {
    background-color: red;
  }
`;
Result
container-type: inline-size;

@container (min-width: $MD_SIZE) {
  background-color: red;
}

Size

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.size.up("md")} {
    background-color: red;
  }
`;
Result
container-type: size;

@container (min-width: $MD_SIZE) {
  background-color: red;
}

Normal

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.normal.up("md")} {
    background-color: red;
  }
`;
Result
container-type: normal;

@container (min-width: $MD_SIZE) {
  background-color: red;
}

Query without type

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.query.up("md")} {
    background-color: red;
  }
`;
Result
@container (min-width: $MD_SIZE) {
  background-color: red;
}

Container queries

Min-width

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md")} {
    background-color: red;
  }
`;
Result
container-type: inline-size;

@container (min-width: $MD_SIZE) {
  background-color: red;
}

Max-width

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.down("md")} {
    background-color: red;
  }
`;
Result
container-type: inline-size;

@container (max-width: $MD_SIZE) {
  background-color: red;
}

Exact breakpoint

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.only("md")} {
    background-color: red;
  }
`;
Result

Whether find next largest size

container-type: inline-size;

@container (min-width: $MD_SIZE) and (max-width: $NEXT_SIZE - 0.2) {
  background-color: red;
}

Else

container-type: inline-size;

@container (min-width: $MD_SIZE) {
  background-color: red;
}

Between breakpoint

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.between(["sm", "md"])} {
    background-color: red;
  }
`;
Result
container-type: inline-size;

@container (min-width: $SM_SIZE) and (max-width: $MD_SIZE - 0.2) {
  background-color: red;
}

Only Attrs

With this method you get only container attrs

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.attrs()}
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.size.attrs()}
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.attrs("name")}
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.size.attrs("name")}
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.query.attrs("name")}
`;
Results
container-type: inline-size;
container-type: size;
container-type: inline-size;
container-name: name;
container-type: size;
container-name: name;
container-name: name;

Only Query

With this method you get queries without type (ex: up, down, only and between)

Attrs method also can be used)

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.query.up("md")} {
    background-color: red;
  }
`;
Result
@container (min-width: $MD_SIZE) {
  background-color: red;
}

Named container

Container name

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md", "name")} {
    background-color: red;
  }
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.between(["sm", "md"], "name")} {
    background-color: red;
  }
`;
Results
container-type: inline-size;
container-name: name;

@container (min-width: $MD_SIZE) {
  background-color: red;
}
container-type: inline-size;
container-name: name;

@container (min-width: $SM_SIZE) and (max-width: $MD_SIZE - 0.2) {
  background-color: red;
}

Container context

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md", ".context")} {
    background-color: red;
  }
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.between(["sm", "md"], ".context")} {
    background-color: red;
  }
`;
Results
container-type: inline-size;

@container context (min-width: $MD_SIZE) {
  background-color: red;
}
container-type: inline-size;

@container context (min-width: $SM_SIZE) and (max-width: $MD_SIZE - 0.2) {
  background-color: red;
}

Container name and context

1. Simple example

const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md", "name.context")} {
    background-color: red;
  }
`;
const Container = styled.div`
  width: 100%;

  ${({ theme }) =>
    theme.container.inline.between(["sm", "md"], "name.context")} {
    background-color: red;
  }
`;
Results
container-type: inline-size;
container-nane: name;

@container context (min-width: $MD_SIZE) {
  background-color: red;
}
container-type: inline-size;
container-name: name;

@container context (min-width: $SM_SIZE) and (max-width: $MD_SIZE - 0.2) {
  background-color: red;
}

2. Complex example

styled.ts

import styled from "styled-components";

export const Container = styled.div`
  width: 100%;

  ${({ theme }) => theme.container.inline.up("md")} {
    p {
      background-color: red;
    }
  }
`;

export const SubContainer = styled.div`
  width: 50%;

  ${({ theme }) => theme.container.inline.up("md", "container")} {
    background-color: pink;
  }
`;

export const SubSubContainer = styled.div`
  ${({ theme }) => theme.container.inline.up("md", ".container")} {
    background-color: yellow;
  }
`;

component.tsx

import * as S from "./styled.ts";

const Component = () => (
  <S.Container>
    <p>container</p>
    <S.SubContainer>
      <S.SubSubContainer>
        <p>sub-sub-container</p>
      </S.SubSubContainer>
    </S.SubContainer>
  </S.Container>
);
Result container-name-and-context-example

How to contribute

To contribute, make sure to follow the steps bellow:

  1. Create a new branch:

     git checkout -b feat/your-new-feature
  2. Make your changes, add unit tests (with jest) and test with npm link

    On styled-container-queries project:

     npm link

    On your app/project:

     npm link styled-container-queries

    This will create a symlink into your node_modules app, and you can test iteratively. You can check more about npm-link here

  3. Before to push your changes to origin, open your pull request and fill all required fields.

    1. Make sure to fill the Release section with what your pull request changes. This section is required to merge pull request.
  4. Set a required semver label according to your change:

    1. semver:patch: used when you submit a fix to a bug, enhance performance, etc;
    2. semver:minor: used when you submit a new component, new feature, etc;
    3. semver:major: used when you submit some breaking change, etc;
    4. semver:prerelease: used when you submit a prerelease (ex: 1.0.0-beta.1);
    5. semver:bypass: used to update docs, or something that doesn’t affect the build.

Info: Once you have merged your pull request, with all required fields, GitHub Actions will be responsible to create a new build and publish.

License

MIT License