• This repository has been archived on 02/May/2023
  • Stars
    star
    121
  • Rank 293,924 (Top 6 %)
  • Language Reason
  • License
    MIT License
  • Created about 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

BuckleScript bindings to Emotion

bs-emotion

Warning This repository is no longer maintained, as we don't use it anymore at Ahrefs. For alternatives, check styled-ppx or bs-css.

bs-emotion status bs-emotion-ppx status

BuckleScript bindings to Emotion.

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:

# yarn
yarn add @ahrefs/bs-emotion
# or npm
npm install --save @ahrefs/bs-emotion

Then add it to bsconfig.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:

# yarn
yarn add --dev @ahrefs/bs-emotion-ppx
# or npm
npm install --save-dev @ahrefs/bs-emotion-ppx

Then add it to bsconfig.json:

"ppx-flags": ["@ahrefs/bs-emotion-ppx/ppx -as-ppx"],

Usage

Defining styles

There are 2 ways to define a CSS class:

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.

(* 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

/* Component.re */

module Css = ComponentStyles;

let component = ReasonReact.statelessComponent(__MODULE__);

let make = _ => {
  ...component,
  render: _ => <div className=Css.container> ... </div>,
};

Composing classnames

Cx

This package provides Cx.merge function which is a binding to Emotion's cx. It merges 2 Emotion's CSS classes into single unique class. See the Caveats section for details.

Cn

Also, there is 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:

Cn.make([Css.one, Css.two]) /* => "css-<HASH>-one css-<HASH>-two" */
Cx.merge([|Css.one, Css.two|]) /* => "css-<HASH>-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
<div className={Cx.merge([|Css.foo, Css.bar|])}>
  <button className=Css.button />
</div>
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:

<div className={Cn.make([Css.foo, Css.bar])} />
Caveat #2
let make = (~className, children) => {
  ...component,
  render: _ =>
    <div className={Cn.make([Css.foo, className])}>
      ...children
    </div>,
};

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.

<div className={Cx.merge([|Css.foo, className|])} />

Contributing

See CONTRIBUTING.md.

Thanks

More Repositories

1

atd

Static types for JSON APIs
OCaml
316
star
2

ocannl

OCANNL: OCaml Compiles Algorithms for Neural Networks Learning
OCaml
66
star
3

rust-ocaml-derive

Rust
61
star
4

melange-recharts

Reason
59
star
5

ahrefs-api-php

PHP
49
star
6

bs-aws-lambda

aws lambda bucklescript bindings
Reason
38
star
7

monorobot

github monorepo bot - configurable directory tree notifications in slack
OCaml
33
star
8

melange-atdgen-codec-runtime

OCaml
29
star
9

devkit

OCaml
29
star
10

hello-native-melange

Skeleton of a shared library for melange and native OCaml/Reason
OCaml
29
star
11

ocaml-ahrocksdb

OCaml bindings for RocksDB
OCaml
21
star
12

jsonschema2atd

Generate ATD types from a JSON schema / OpenAPI document
Perl
19
star
13

bs-reactstrap

Reason
18
star
14

atdgen-workshop-starter

Starter project for ReasonConf atdgen workshop
JavaScript
16
star
15

ocaml-errortrace

OCaml
16
star
16

bs-react-select

ReasonML bindings for react-select
Reason
16
star
17

melange-fest

A minimal test framework for Melange using Node test runner
OCaml
15
star
18

bs-dotenv

Reason
14
star
19

melange-react-dnd

Makefile
14
star
20

melange-react-intl

ocaml ppx to reduce boilerplate from react-intl messages
Reason
13
star
21

ocaml-qfs

OCaml bindings to libqfs
OCaml
13
star
22

ocaml-ubpf

OCaml bindings for the userspace eBPF VM
C
11
star
23

vim-styled-ppx

styled-ppx highlighting support for vim
Vim Script
9
star
24

esgg

ElasticSearch Guided (code) Generator
OCaml
9
star
25

ocaml-elastic

Elasticsearch http client library
OCaml
9
star
26

ppx_deriving_variant_string

OCaml PPX deriver that generates converters between regular or polymorphic variants and strings
OCaml
9
star
27

ocaml-jemalloc

OCaml
8
star
28

melange-react-dates

ReasonML bindings for react-dates
Reason
8
star
29

slack

OCaml
7
star
30

ocaml-bpf-match-string

A simple eBPF DSL and assembler
OCaml
7
star
31

ocaml-farmhash

C
6
star
32

lwt_named_threads

OCaml
5
star
33

react-alicante-workshop

Public repo for the "Write maintainable React applications using OCaml" React Alicante workshop
HTML
4
star
34

ocaml-ceph

OCaml bindings to libcephfs
OCaml
4
star
35

lwt_comm

OCaml
4
star
36

scraps

miscellanea
OCaml
4
star
37

lite-flag-icon-css

Lite version of flag-icon-css from http://flag-icon-css.lip.is
CSS
4
star
38

reshowcase

A tool to create demos for your ReasonReact components
Reason
4
star
39

passage

store and manage access to shared secrets
OCaml
3
star
40

LongCamel

Python
3
star
41

talks

3
star
42

melange-numeral

Reason
3
star
43

bs-highcharts

BuckleScript bindings for Highcharts JS
OCaml
3
star
44

bs-highcharts-react

Bucklescript bindings for highcharts-react-official
OCaml
3
star
45

opam-check-npm-deps

An opam plugin that checks the opam switch to gather npm packages in depexts to check against installed packages in node_modules.
Perl
3
star
46

ocaml-murmur3

OCaml bindings for murmur3 hash function (with vendored C code)
C
2
star
47

lib-ocamlnet3

https://gitlab.com/gerdstolpmann/lib-ocamlnet3.git
OCaml
1
star
48

vscode-ocp-grep

TypeScript
1
star