https://github.com/ahrefs/bs-emotion
BuckleScript bindings to Emotion
https://github.com/ahrefs/bs-emotion
Last synced: 5 months ago
JSON representation
BuckleScript bindings to Emotion
- Host: GitHub
- URL: https://github.com/ahrefs/bs-emotion
- Owner: ahrefs
- License: mit
- Archived: true
- Created: 2018-11-01T09:09:44.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-05-02T15:22:55.000Z (almost 3 years ago)
- Last Synced: 2024-09-25T23:11:20.897Z (over 1 year ago)
- Language: Reason
- Size: 1.02 MB
- Stars: 121
- Watchers: 5
- Forks: 13
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - bs-emotion
README
# bs-emotion
> **Warning**
> This repository is no longer maintained, as we don't use it anymore at Ahrefs. For alternatives, check [styled-ppx](https://github.com/davesnx/styled-ppx/) or [bs-css](https://github.com/giraud/bs-css).
BuckleScript bindings to [Emotion](https://emotion.sh).
## BuckleScript v8+
`bs-emotion` and `bs-emotion-ppx` are compatible with BuckleScript v7 starting from version 2.0.0.
## Previous versions
Use v1.0.1 if you need compatibility with BuckleScript v7.
If you need to use them with BuckleScript v5 or below, install the earlier versions:
- `@ahrefs/bs-emotion` version ^0.1.2
- `@ahrefs/bs-emotion-ppx` version ^0.0.7
## Installation
Get the package:
```shell
# yarn
yarn add @ahrefs/bs-emotion
# or npm
npm install --save @ahrefs/bs-emotion
```
Then add it to `bsconfig.json`:
```json
"bs-dependencies": [
"@ahrefs/bs-emotion"
]
```
### `bs-emotion-ppx`
If you want to auto-label generated classnames for easier debugging, you can install `bs-emotion-ppx`:
```shell
# yarn
yarn add --dev @ahrefs/bs-emotion-ppx
# or npm
npm install --save-dev @ahrefs/bs-emotion-ppx
```
Then add it to `bsconfig.json`:
```json
"ppx-flags": ["@ahrefs/bs-emotion-ppx/ppx -as-ppx"],
```
## Usage
### Defining styles
There are 2 ways to define a CSS class:
```ocaml
open Emotion
(* If you use ppx *)
let button = [%css [ ... ]] (* -> "css-HASHED-button" *)
(* If you don't use ppx *)
let button = css [ ... ] (* -> "css-HASHED" *)
```
And here's real-world example:
> It's in OCaml syntax, but you can use Reason too.
```ocaml
(* ComponentStyles.ml *)
open Emotion
let container = [%css [
display `flex;
flexFlow `column `nowrap;
alignItems `center;
]]
let shape = [%css [
display `flex;
flexFlow `row `nowrap;
alignItems `center;
justifyContent `center;
transitionProperty "border-radius";
transitionDuration (`ms 100);
transitionTimingFunction `easeInOut;
width (`px 200);
height (`px 200);
borderRadius (`px 6);
backgroundColor (`hex "29d");
(* :hover selector, same as `select ":hover" [ ... ]` *)
hover [
borderRadius (`pct 50.);
important (cursor `grab);
];
]]
(* Dynamic styling *)
(* NOTE: ppx supports functions with max 2 arguments *)
let text ~size = [%css [
color (`hex "fff");
fontSize (`px size);
fontWeight 700;
(* Transition takes property, duration, timing-function & delay *)
transition "font-size" (`ms 100) `easeInOut `zero;
(* You can define multiple transitions by packing them into list of tuples *)
transitions [
("font-size", `ms 100, `easeInOut, `ms 0);
];
(* Complex selector that uses .container class defined above *)
(* Rendered as: `.container:hover .text {...}` *)
select {j|.$container:hover &|j} [
fontSize Calc.(((`px size) + (`pct 150.)) * (`n 1.5));
];
(* @media quiery with nested selectors *)
media "(max-width: 900px)" [
color (`hex "ff69b4");
select ":hover" [
color (`hex "fff");
];
];
]]
(* Define keyframes *)
let bounce = keyframes [
(0, [ transform (`translateY `zero); ]);
(50, [ transform (`translateY (`px (-20))); ]);
(100, [ transform (`translateY `zero); ]);
]
let animated = [%css [
(* Use generated animation name *)
animationName bounce;
animationDuration (`ms 300);
animationIterationCount (`i 7);
]]
(* Compose things *)
let smallText = [%css [
fontSize (`em 0.8);
]]
let note = css ~extend: smallText [
label "note";
marginTop (`px 10);
]
```
### Applying styles
```reason
/* Component.re */
module Css = ComponentStyles;
let component = ReasonReact.statelessComponent(__MODULE__);
let make = _ => {
...component,
render: _ =>
... ,
};
```
### Composing classnames
#### Cx
This package provides `Cx.merge` function which is a binding to Emotion's [`cx`](https://emotion.sh/docs/cx). It merges 2 Emotion's CSS classes into single unique class. See the [Caveats](#caveats) section for details.
#### Cn
Also, there is [`re-classnames`](https://github.com/alexfedoseev/re-classnames). You can use it to combine classnames together. It's not aware of Emotion and simply operates on strings.
#### Caveats
First, let's talk about the difference between `Cn.make` and `Cx.merge`:
```reason
Cn.make([Css.one, Css.two]) /* => "css--one css--two" */
Cx.merge([|Css.one, Css.two|]) /* => "css--one-two" */
```
If the former simply concatenates two classname strings into a single string (and as a result 2 CSS classes are applied) then the latter merges 2 Emotion classes into single unique class and applies it to a node (as a result 1 unique CSS class is applied).
##### Caveat #1
```reason
```
```ocaml
let foo = css [ ... ]
let bar = css [ ... ]
let button = css [
...
select {j|.$foo:hover &|j} [
(*
It won't work due to `.foo` class is being merged w/ `.bar`
into single unique classname inside component
*)
]
]
```
To make this css work you can use `Cn.make`, e.g:
```reason
```
##### Caveat #2
```reason
let make = (~className, children) => {
...component,
render: _ =>
...children
,
};
```
Oftentimes, UI abstractions accept `className` prop to extend or override default CSS of abstraction. Since position of classname on application site doesn't guarantee precedence of `className` prop (in CSS, precedence is determined by position of classname on definition site), it's not safe to use `Cn.make` here. In this case, use `Cx.merge` since `Emotion` determines precedence on application site and guarantees that last classname has precedence over the preceding classes.
```reason
```
## Contributing
See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
## Thanks
- [`glennsl/bs-typed-css-core`](https://github.com/glennsl/bs-typed-css-core) and [`SentiaAnalytics/bs-css`](https://github.com/SentiaAnalytics/bs-css) for examples and inspiration.