Rita Krutikova
Correct me if I say something weird!
Excitement! Fun idea, let's implement!
Setup, modelling data, api
Comic Sans, Times New Roman, Arial
3px, 15px, 23rem
red, green, black
Too many choices!
Too difficult to make a decision
No progress
CSS is not Reason 😨
Not a flashy UI
Consistent and structured UI
Define base values - derive rest
Develop faster
Nice DX and fun coding (CSS-in-Reason!)
Safe refactoring with compiler help
module Tokens = {
type brandColor = Primary | Secondary;
type textColor = Light | Dark;
let colorToString =
fun
| Primary => "blue"
| Secondary => "darkgray"
| Light => "gray"
| Dark => "black";
}
module Tokens = {
type space = Xs | Sm;
type fontSize = Sm | Body;
let spaceToPx =
fun
| Xs => 8
| Sm => 16;
}
let spacePx = Tokens.spaceToPx(Tokens.Sm);
type brandColor = [ | `Primary | `Secondary];
type textColor = [ | `Light | `Dark];
let colorToString =
fun
| `Primary => "blue"
| `Secondary => "darkgray"
| `Light => "gray"
| `Dark => "black";
type space = [ | `xs | `sm];
type fontSize = [ | `sm | `base];
let spaceToPx =
fun
| `xs => 8
| `sm => 16;
let spacePx = Tokens.spaceToPx(`sm);
Conciseness and convenience of use
Same constructors across types are allowed
Mix constructors
type color = [
| `Primary
| `Error
| `BodyBg
| `BodyText
];
type fontSize = [ | `sm | `base | `md | `lg];
type spacing = [ | `xs | `sm | `md | `lg ];
// line heights, transitions, shadows etc.
let color =
fun
| `Primary => "#1f3a48"
| `Error => "#f44336"; // rest
let fontSize =
fun
| `sm => 12
| `base => 16; // rest
let spacing =
fun
| `xs => 4
| `sm => 8
| `md => 16;
Experimenting
let baseFontSizePx = 20;
let lineHeight =
fun
| `body => 1.4
| `heading => 1.2;
How do I define other font sizes? 🤔
fontSize = (ratio ^ factor) * baseFontSize
let baseFontSizePx = 20;
let ratio = 1.25;
let fontScale = factor =>
ratio ** factor
*. (baseFontSizePx |> float_of_int);
let fontSize =
fun
| `sm => fontScale(-1) // label
| `base => fontScale(0) // body
| `md => fontScale(1) // h3
| `lg => fontScale(2) // h2
| `xl => fontScale(3) // h1
let lineHeight =
fun
| `body => 1.4
| `heading => 1.2;
let baseFontSizePx = 20;
let baseLineHeight = 1.4; // 20 * 1.4 = 28px
let grid = 4;
let space =
fun
| `xs => grid
| `sm => grid * 2
| `md => grid * 4
| `lg => grid * 6;
<div
className={style([
color(`hex("111")),
marginBottom(`px(16)),
fontSize(`px(16)),
lineHeight(`abs(1.4)),
])}>
{React.string("Welcome!")}
</div>
open Css;
let make = children => {
let className =
style([
color(`BodyText |> Theme.color),
marginBottom(`sm |> Theme.space),
fontSize(`base |> Theme.fontSize),
lineHeight(`body |> Theme.lineHeight),
]);
<p className> children </p>;
};
open Theme;
let btnStyles = size => {
let (pd, font) =
switch (size) {
| `Small => (space(`sm), fontSize(`sm))
| `Medium => (space(`md), fontSize(`base))
};
Css.[padding(pd), fontSize(font)];
};
type size = [ | `Small | `Medium];
[@react.component]
let make = (~size=`Medium, ~children) => {
<button className=Css.style(btnStyles(size))>
children
</button>;
};
<Button size=`Small onClick=...>
{React.string("I am so small...")}
</Button>
Button (`Primary, `Secondary)