@klosser/responsive-style
Functions that take a value or multiple values grouped by breakpoints, and returns an object with CSS declarations and media queries that can be passed to a CSS-in-JS library of choice.
createResponsiveStyle('blue', => color => ({ color }))
// ({ color: blue })
createResponsiveStyle(['blue', { 700: 'red' }], color => ({ color }))
/*({
color: blue,
'@media (min-width: 700px)': {
color: 'red'
}
})*/
You can also map the values to something else
createResponsiveStyle(color, value => thmeColors[value])
createResponsiveStyle(size, value => ({ textSize: value }))
createResponsiveStyle(variant, value => ({
backgroundColor: variant === 'dark' ? 'black' : 'white',
'--text-color': variant !== 'dark' ? 'black' : 'white',
}))
Extractors
Sometimes you want to allow a style like margin or padding to be expressed like:
{ margin: 4 }
{ margin: '30px auto' }
{ margin: { top: 4, x: 'auto' } }
{ margin: [{ y: 8 }, { 400: { top: 20 }}] }
Create a reuseable mapping function that you use in your base components, or use the one provided with in this package.
import { BoxSpacingMapper, createResponsiveStyle } from '@klosser/responsive-style'
createResponsiveStyle(margin, BoxSpacingMapper)
createResponsiveStyle(padding, BoxSpacingMapper)
Media queries
Media queries can either be written manually which can like:
;[
{ color: 'black' },
{
'@media (min-width: 700px) and (prefers-color-scheme: dark)': {
color: 'white',
},
},
]
or by just passing a number as a breakpoint:
[{ color: 'red', { 500: { color: 'blue' }}}]
By default those breakpoints assume you implement mobile first, thus the breakpoints are set in (min-width: [breakpoint]px)
. This can be changed in (options)[#options].
Predefined media queries
If you pass predefined media queries in (options)[#options] you can set them by breakpoint names instead:
const breakpoints = {
large: 1200,
medium: 600,
small: 460,
}
function Text({
color,
}: {
color: ResponsiveStyle<keyof typeof themeColors, keyof typeof breakpoints>
}) {
return (
<div
className={css(
createResponsiveStyle(color, value => themeColors[value]),
{ breakpoints }
)}
/>
)
}
;<Text color={['purple-20', { large: 'orange-20' }]} />
Media query collisions
If you merge responsive styles to an object like this example:
className={css({
...createResponsiveStyle(color, value => themeColors[value]),
...createResponsiveStyle(display),
})}
If both responsive styles include the same media query, only the last one will be included. Either:
- Pass a key as the third argument:
createResponsiveStyle(color, value => themeColors[value], 'color')
- Create unique classnames:
cs(css(createResponsiveStyle(display), createResponsiveStyle(textSize)))
Examples
This is a basic example of how you can create a reuseable component that can take a color as a prop, or multiple grouped by breakpoints. The rest of the examples will be shown in tsx.
import React from 'react'
import css from 'emotion'
import { createResponsiveStyle } from '@klosser/responsive-style'
export default function Text({ color, ...rest }) {
return <div className={css(createResponsiveStyle(color => ({ color })))} {...rest} />
}
import Text from './Text'
export const Example = () => (
<Text color={['magenta', { 700: 'criomson', 1200: 'black' }]}>Hello World!</Text>
)
<style>
.gwe3w {
color: magenta;
}
@media (min-width: 700px) {
.gwe3w {
color: criomson;
}
}
@media (min-width: 1200px) {
.gwe3w {
color: black;
}
}
</style>
<div class="gwe3w">Hello World!</div>
Margin & padding
import Box from './Box'
export const Example = () => (
<Box
backgroundColor="magenta-20"
margin={[
{ top: 20 },
{
700: {
top: 40,
bottom: 20,
},
},
]}
/>
)
Custom mapping
import Box from './Box'
export const Example = () => (
<Box
backgroundColor="magenta-20"
margin={[
{ top: 20 },
{
700: {
top: 40,
bottom: 20,
},
},
]}
/>
)