Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
John Persson committed Oct 22, 2017
2 parents de9ba4b + 1000dfc commit d55f01f
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 100 deletions.
154 changes: 68 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
[![Travis](https://img.shields.io/travis/rust-lang/rust.svg)]()

## Styled Components Breakpoint 💅
This package provides a friendly API for working with breakpoints in [Styled Components](https://www.styled-components.com/). It allows you to set up any number of breakpoints with a naming convention of your choice.
Once set up you'll have three main ways of interacting with your breakpoints `up` (min-width), `down` (max-width), and `only` (a range between two breakpoints).
This library provides utility functions for dealing with media queries, to make them reusable and easier to read. It can be used as an alternative to SASS/LESS mixins.

More on mixins and [Styled Components](https://www.styled-components.com/) in [this article](https://github.com/styled-components/styled-components/blob/master/docs/tips-and-tricks.md).

---

Expand All @@ -17,25 +18,14 @@ npm install @humblebee/styled-components-breakpoint
```
---

### Usage and example

The default export of `styled-components-breakpoint` is a function that accepts a config object with your breakpoints. This will return an instance with utility functions for each breakpoint.
I'd recommend that you store this instance in a separate file that can later be imported and used by any component.
### Usage and setup

Coming from form SASS I like to think of these kind of utility functions for `styled-components` in terms of `mixins`. I also think it makes seance to keep these "mixins" close to our projects themes (if any exist), to make them easier to discover when working on other styles. With this in mind the folder structure could look something like this:
The default export of `styled-components-breakpoint` is a function that accepts a `config` object of breakpoints. This will return an object with three main utility methods/mixins: `up` (min-width), `down` (max-width) and `only` (a range between two media queries), all described in detail below.

.
+--componsnts
| +--Button.js
+--themes
| +--mixins.js


`themes/mixins.js`
```javascript
import styledBreakpoint from '@humblebee/styled-components-breakpoint';

// Create an instance of styled-components-breakpoint and pass it an object of breakpoints.
// Creates an object with breakpoint utility methods.
export const breakpoint = styledBreakpoint({
xs: 320,
s: 576,
Expand All @@ -45,104 +35,96 @@ export const breakpoint = styledBreakpoint({
});
```

`components/Button.js`
#### Up
```javascript
// Styled Components
import styled from 'styled-components';
// Our breakpoint instance/mixin
import { breakpoint } from '../../theme/mixins';
breakpoint.up('m')
// Will return a media query with a min-width of 768
// @media only screen and (min-width: 768px)
```

const Button = styled.button`
background: white;
${breakpoint.m}`
background: palevioletred;
`
`
});
#### Down
```javascript
breakpoint.down('m')
// Will return a media query with a max-width of 768
// @media only screen and (max-width: 768px)
```

#### Only
```javascript
breakpoint.only('m')
// Will return a range media query between "m" and the next upper breakpoint "l"
// @media only screen and (min-width: 768px) and (max-width: 1200px)
```

In the above example we create an instance of `styled-components-breakpoint` in a file called `themes/mixins.js` and export it with the name `breakpoint`.
We then import `breakpoint` in a separate file called `components/Button.js` and use it inside the styling of our `styled-component` button.
The function `breakpoint.m` will result in the following media query: `'@media only screen and (min-width: 768px)'`, giving our button component a `background-color` of `palevioletred` when the viewport is wider than 768px.
```javascript
breakpoint.only('m', 'xl')
// Will return a range media query between "m" and the breakpoint passed as the second argument, "xl"
// @media only screen and (min-width: 768px) and (max-width: 1200px)
```

## API
#### Shorthand

Continuing on the above example, you have access to all the other breakpoints in the same manner, in our case: `breakpoint.xs`, `breakpoint.s`, `breakpoint.m`, `breakpoint.l`, `breakpoint.xl`. As mentioned in the intro you can add as many breakpoints as you like with any naming convention you prefer. If you prefer the naming convention used in Twitter Bootstrap, your config object would look like this.
There is also a shorthand for mobile first media queries (min-width). Calling `breakpoint.m` is the same as `breakppoint.up('m')`.

```javascript
export const breakpoint = styledBreakpoint({
xs: 320,
sm: 576,
md: 768,
lq: 992,
xl: 1200,
});
`breakpoint.m'`
// Will return a media query with a min-width of 768
// @media only screen and (min-width: 768px)
```

---

#### breakpoint.up.m
### Usage with styled components
In the following example we create a styled button component.

In the "Usage and example" section we made use of the function `breakpoint.m`, this is a shorthand for writing `breakpoint.up.m`. The reason for this shorthand is to encourage the usage of mobile-first breakpoints, i.e. `min-width` media queries.

The functions `breakpoint.m` and `breakpoint.up.m` are the same.
This is the folder structure we'll be working with.
```
.
+--components
| +--Button.js
+--themes
| +--mixins.js
```

`themes/mixins.js`
```javascript
const Button = styled.button`
background: white;
${breakpoint.up.m}`
background: palevioletred;
`
`
import styledBreakpoint from '@humblebee/styled-components-breakpoint';

// Create an instance of styled-components-breakpoint and pass it an object of breakpoints.
export const breakpoint = styledBreakpoint({
xs: 320,
s: 576,
m: 768,
l: 992,
xl: 1200,
});
```

#### breakpoint.down.m

In contrast to `breakpoint.up`, `breakpoint.down` goes the opposite direction and returns a `max-width` media query. The example below would return the media query `@media only screen and (min-width: 768px)`.

`components/Button.js`
```javascript
// Styled Components
import styled from 'styled-components';
// Our breakpoint instance/mixin
import { breakpoint } from '../../theme/mixins';

const Button = styled.button`
background: white;
${breakpoint.down.m}`
font-size: 18px;
${breakpoint.down(s)}`
font-size: 12px;
`
${breakpoint.m}`
background: palevioletred;
`
`
});
```

#### breakpoint.only.m(breakpoint?)

Unlike `up` and `down`, the `only` function accepts an optional breakpoint argument in the form of a string. This argument is used to return a range media query, between the breakpoint used in the executing function and the passed argument.
For example, executing `breakpoint.only.m('xl')`, will return a range between the `m` and `xl` breakpoints.
```javascript
`breakpoint.only.m('xl')`
// Will return:
// @media only screen and (min-width: 768px) and (max-width: 1200px)
```

It works just as well to pass a smaller breakpoint than the executing function. For example starting at breakpoint `m` and going down to `s`.
```javascript
`breakpoint.only.m('s')`
// Will return:
// @media only screen and (max-width: 768px) and (min-width: 576px)
```

If no argument is passed the next upper breakpoint will be used implicitly. For example, executing `breakpoint.only.m()`, will return a range between the `m` and `l` breakpoints.

```javascript
`breakpoint.only.m()`
// Will return:
// @media only screen and (min-width: 768px) and (max-width: 992px)
```

**Important! Even if no breakpoint argument is passed to `only`, it is still necessary to actually execute the function. If not no media query will be returned.**
The first mixin `breakpoint.down(s)`, will give the styled button component a font size of 12px, at a breakpoint lower than "s", i.e. max-width(320px).

**Correct: breakpoint.only.m()**

**Wrong: breakpoint.only.m**
The second mixin `breakpoint.m`, uses the short hand version of `breakpoint.up.('m')`, and will give the button a background of `palevioletred`, at a breakpoint higher than "m", i.e. min-width(768).

---

Happy coding!
/The bees at [Humblebee](http://humblebee.se) 🐝

/ The bees at [Humblebee](http://humblebee.se) 🐝
30 changes: 16 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const getNextMedia = (breakpoints: Breakpoints, width: number): number =>
};

export const mediaRules =
(breakpoints: Breakpoints, widthKey: string, rule: Rule, boundKey: string) => {
(breakpoints: Breakpoints, widthKey: string, rule: Rule, boundKey?: string) => {
const width = breakpoints[widthKey];
const bound = breakpoints[boundKey];
let baseWidthRule = mediaWidthRule(rule);
Expand Down Expand Up @@ -74,16 +74,18 @@ export const mediaRules =
return [].concat([baseRule], boundRule ? [boundRule] : []).join(' and ');
};

export const getMedias = (breakpoints: Breakpoints, rule: Rule, method: boolean = false) => (
Object.keys(breakpoints).reduce((acc, key) => {
export const getMixin = (breakpoints: Breakpoints, key: string, rule: Rule = 'up') => (bound: string) => (
(...args) => css`
${mediaTemplate(mediaRules(breakpoints, key, rule, bound))}{
${css(...args)}
}
`
);

export const getMediaShorthands = (breakpoints, rule, method: boolean = false) => (
Object.keys(breakpoints).reduce((acc: Object, key: string) => {
// Create a method that accepts a bound media
const boundMethod = bound => (
(...args) => css`
${mediaTemplate(mediaRules(breakpoints, key, rule, bound))}{
${css(...args)}
}
`
);
const boundMethod = getMixin(breakpoints, key, rule);

return ({
...acc,
Expand All @@ -94,13 +96,13 @@ export const getMedias = (breakpoints: Breakpoints, rule: Rule, method: boolean
);

export const getMedia = (breakpoints: Breakpoints) => {
const mediasUp = getMedias(breakpoints, 'up');
const mediasUp = getMediaShorthands(breakpoints, 'up');

return ({
...mediasUp,
up: { ...mediasUp },
down: { ...getMedias(breakpoints, 'down') },
only: { ...getMedias(breakpoints, 'only', true) },
up: (widthKey: string) => getMixin(breakpoints, widthKey, 'up'),
down: (widthKey: string) => getMixin(breakpoints, widthKey, 'down'),
only: (widthKey: string, boundKey: string) => getMixin(breakpoints, widthKey, 'only')(boundKey),
});
};

Expand Down

0 comments on commit d55f01f

Please sign in to comment.