I'm trying to figure out how to resolve a styled-system function within a component by default. The idea is to have a container element which uses the theme out of the box to supply the main responsive breaks.
Right now what I am doing below is supplying the styled-system width
function with an object like this:
//theme.js
const CONTAINERS = {
xs: "100%",
sm: "55rem",
md: "74rem",
lg: "100rem",
xl: "131rem",
};
export default {
containers: CONTAINERS,
//other items
}
//Container.js
export default (props) => {
const { containers, grid } = useContext(ThemeContext);
return (
<Container px={grid.margin} width={containers} {...props}>
{props.children}
</Container>
);
};
const Container = styled("div")(
css`
margin-right: auto;
margin-left: auto;
padding-right: 1rem;
padding-left: 1rem;
`,
compose
);
This DOES work, however, it's not as clean as I would like it. I would love to be able to simply have
const Container = styled("div")(
css`
margin-right: auto;
margin-left: auto;
padding-right: 1rem;
padding-left: 1rem;
${//maybe resolve the responsive widths here}
`,
compose //<- a group of styled-system functions supplied in one object
//or resolve responsive widths here
);
export default Container
This would be so much cleaner as I could then combine and export layout components into one file
without having to do the const Container...
+ const StyledContainer...
formalities.
I'm toying with the idea of writing a function which loops the containers
object and returns the widths wrapped in media queries but I'm wondering if styled-system does this out of the box?
I can't find anything in the docs to make something like this work out of the box so I've implemented it manually.
//theme.js
const BREAKPOINTS = {
xs: "0em",
sm: "37em",
md: "48em",
lg: "64.625em",
xl: "84em",
};
const CONTAINERS = {
xs: "100%",
sm: "55rem",
md: "74rem",
lg: "100rem",
xl: "131rem",
};
The script below takes the objects above and puts the breakpoints values into a wrapping @media
then inserts the values of the containers as width:
inside of that. Then returns the entire lot as css.
const Container = styled("div")(
css`
margin-right: auto;
margin-left: auto;
padding-right: 1rem;
padding-left: 1rem;
&::after,
&::before {
content: "";
display: block;
min-height: 2rem;
height: 4vw;
max-height: 6rem;
}
`,
compose,
(props) => {
const breakpoints = props.theme.breakpoints;
const containers = props.theme.containers;
const makeMedia = (media) => {
return (...args) => css`
@media ${media} {
${css(...args)}
}
`;
};
const getMedia = Object.keys(breakpoints).reduce((media, breakpoint) => {
const breakpointWidth = breakpoints[breakpoint];
media[breakpoint] = makeMedia(
[breakpoint !== 0 && `(min-width: ${breakpointWidth})`]
.filter(Boolean)
.join(" and ")
);
return media;
}, {});
return css`
${Object.keys(breakpoints).map(
(key) => containers[key] && getMedia[key]`width: ${containers[key]}`
)};
`;
}
);