Responsive Styles
A concise syntax for writing mobile-first, responsive styles
Overview
When building web applications, writing, managing and keeping track of extensive media queries and responsive styles can often become tedious.
In an attempt to mitigate this, Jynx
offers a convenient alternative, allowing you to
efficiently and consistently define mobile-first, responsive styles, right from your
tsx
/jsx
.
Any prop within a style function can accept responsive styles and they can be written as
both objects and arrays. If a prop is associated with a theme
scale, each value in the
responsive style will also have access to that scale.
<Container p={[16, 24, 32]}/>
<Container color={['red', 'green', 'blue']}/>
<Container
justifyContent={{
_: 'flex-start',
sm: 'center',
lg: 'space-between'
}}
/>
How it works
Responsive arrays and objects are parsed by mapping the provided styles to 'min-width' media queries, generated from breakpoints in the current theme.
The result is a series of mobile-first style declarations in the form of a
CSSObject
that can be interpreted by most CSS-in-JS
libraries.
As an example, a component like this:
<Container p={[16, 24, 32]} />
outputs an object like this:
{
padding: "16px",
"@media screen and (min-width: 640px)": {
padding: "24px"
},
"@media screen and (min-width: 960px)": {
padding: "32px"
}
}
which would be rendered by a CSS-in-JS library like this:
.Container__class {
padding: 16px;
}
@media screen and (min-width: 640px) {
.Container__class {
padding: 24px;
}
}
@media screen and (min-width: 960px) {
.Container__class {
padding: 32px;
}
}
Responsive Arrays
The simplest way to define a responsive style is as an array.
A responsive array must have at least one item to use as the base style, then any subsequent items are sequentially mapped to media queries generated from the relative 'next' breakpoint in the scale.
<Container
p={[
16, /* used as the base value */
24, /* mapped to the first media query */
32 /* mapped to the second media query */
]}
/>
Skipping breakpoints
Due to the nature of arrays, the order the values are written in, will be the same order in which they are mapped to the breakpoints.
If a style persists across multiple consecutive media queries, you can use null
to skip
over that breakpoint.
<Container p={[16, 24, null, 32]} />
{
padding: "16px",
"@media screen and (min-width: 640px)": {
padding: "24px"
},
// skips 960px and goes straight to 1280px
"@media screen and (min-width: 1280px)": {
padding: "32px"
}
}
Responsive Objects
An alternative way to define responsive styles is as objects.
Like responsive arrays, responsive objects must have at least one value, this time
assigned to the key "_"
.
However, values are not required to be passed sequentially, and instead can be assigned to
a keys referencing the key of the desired breakpoint from theme.breakpoints
.
<Container
fontSize={{
_: 24, /* used as the base value */
sm: 32, /* mapped to media query generated from `sm` breakpoint */
lg: 48 /* mapped to media query generated from `lg` breakpoint */
}}
/>
Breakpoints, by default, are taken from the default theme, however you can easily override these with your own values.
This is useful in situations where you need to skip
multiple breakpoints as it's often
easier to write than an array with several null
values.
/* unfavored — excessive null values */
<Container m={['0 16px', null, null, null, null, '0 auto']} />
/* preferred — more legible and concise */
<Container m={{ _: '0 16px', xxl: '0 auto' }} />
Breakpoints
Media queries are generated from the breakpoints
defined in the current theme
. If you
choose not to specify a custom theme
then the values are taken from the default theme
.
/* Default Breakpoints */
export const theme = {
breakpoints: {
sm: 640,
md: 960,
lg: 1280,
xl: 1440,
xxl: 1600
}
}
Based on this, the full CSS output generated would resemble the following:
.Styled__class {
/* value at [0] or ._ */
}
@media screen and (min-width: 640px) {
.Styled__class {
/* value at [1] or .sm */
}
}
@media screen and (min-width: 960px) {
.Styled__class {
/* value at [2] or .md */
}
}
@media screen and (min-width: 1280px) {
.Styled__class {
/* value at [3] or .lg */
}
}
@media screen and (min-width: 1440px) {
.Styled__class {
/* value at [4] or .xl */
}
}
@media screen and (min-width: 1600px) {
.Styled__class {
/* value at [5] or .xxl */
}
}
Custom breakpoints
You can easily use your own breakpoints with responsive styles by
creating a theme
and defining a breakpoints
scale within it.
/* theme.ts */
export const theme = {
breakpoints: {
small: 540,
medium: 720,
large: 1080
}
}
Then wrap your entire app in your CSS-in-JS library's ThemeProvider
, passing to it your
custom theme
in the process.
/* App.ts */
import { ThemeProvider } from 'styled-components'
import { theme } from '../theme.ts'
const App: React.FC = props => (
<ThemeProvider theme={theme}>
{/* application content */}
</ThemeProvider>
)
export default App
You can specify your breakpoints
scale as either an object or an array and can use
strings with units or plain numbers which will be converted to px
.
// numeric, array-based scale
export const theme = {
breakpoints: [540, 720, 1080, 1440]
}
// numeric, object-based scale
export const theme = {
breakpoints: {
mobile: 540,
tablet: 720,
laptop: 1080,
desktop: 1440
}
}
// string, array-based scale
export const theme = {
breakpoints: ['40em', '60em', '80em']
}
// string, object-based scale
export const theme = {
breakpoints: {
mobile: '40em',
tablet: '60em',
laptop: '80em',
desktop: '100em'
}
}
Using an array-based scale with responsive objects
If you choose to define your breakpoints
scale as an array, there's one small caveat to
be aware of, specifically when using responsive objects.
As the array values have no 'key' as such, when you come to access them from within a responsive object, you'll need to use the array index of the desired breakpoint as the key.
export const theme = {
breakpoints: [640, 960, 1280, 1440, 1600]
}
<Component m={{_: 8, "0": 12, "2": 16, "3": 24 }} />
.Component__class {
margin: 8px;
}
@media screen and (min-width: 640px) {
.Component__class {
margin: 12px;
}
}
@media screen and (min-width: 1280px) {
.Component__class {
margin: 16px;
}
}
@media screen and (min-width: 1440px) {
.Component__class {
margin: 24px;
}
}
This can end up becoming problematic as you'll need to remember the order of your breakpoints scale and, should you change that order, your styles could be thrown out of whack.
Therefore, if you're planning on using a lot of responsive objects, it is recommended to
define your breakpoints
scale as an object with appropriately named keys.
Visit the theme config page for a more in-depth look at custom breakpoints.