Skip to content

Commit

Permalink
docs: improve documentation
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Hivert <hivert.is.coming@gmail.com>
  • Loading branch information
ghivert committed Apr 5, 2024
1 parent 3498965 commit d8ae736
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 226 deletions.
213 changes: 159 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,119 @@ Craft does not try to add complicated API on top of CSS. If you have CSS
knowledge, you'll feel right at home, with all the niceties offered by
Craft, i.e. type-checking of sizes and push-to-browser stylesheets of your
classes, as well as SSR support.
Craft has currently two run modes: directly in your browser and leverages on
all abilities of the JS runtime, and on backend, to leverages on SSR.
Craft has currently to way to use it: directly in your vanilla Gleam
application or in your fully-featured [Lustre](https://hexdocs.pm/lustre/) application.
Craft allows you to build two types of CSS classes: dynamic ones, changing

Craft has currently only one run mode: directly in your browser to leverage on
all abilities of the JS runtime.
It also allows you to build two types of CSS classes: dynamic ones, changing
over time, and static ones, compiled once and for all, and reused during the
entire lifetime of the application, just like classic CSS stylesheets.

Craft is thought to interact nicely with [Lustre](https://hexdocs.pm/lustre/),
but can also be used directly in your vanilla Gleam application or in your
fully-featured application. This should probably only be considered to create
custom framework or to integrate Craft in your favorite framework, because Craft
has its own lifecycle to render styles. More informations can be found in the docs.

## Installation

Craft is published on [Hex](https://hex.pm/packages/craft). Add it to your
project by using the gleam CLI.

```bash
gleam add craft
```

## Setup

If you're using Lustre (which is strongly recommended), you can just use the
[`lustre_setup`](https://hexdocs.pm/craft/craft.html#lustre_setup) function.

Otherwise, you have to follow the lifecycle of Craft, and use the three low-level
functions [`create_cache`](https://hexdocs.pm/craft/craft.html#create_cache),
[`prepare`](https://hexdocs.pm/craft/craft.html#prepare) and [`render`](https://hexdocs.pm/craft/craft.html#render).
Create the cache with [`create_cache`](https://hexdocs.pm/craft/craft.html#create_cache)
and before every repaint of your frontend, call [`prepare`](https://hexdocs.pm/craft/craft.html#prepare).
After the repaint, synchronously, call [`render`](https://hexdocs.pm/craft/craft.html#render),
and let the magic happen in your browser. Heads up in the docs for more details.

## Example with Lustre

```gleam
import craft
import craft/options as craft_options
import gleam/int
import lustre
import lustre/element.{text}
import lustre/element/html.{div, button, p}
import lustre/event.{on_click}
pub fn main() {
let assert Ok(render) = craft.setup(craft_options.dom())
let app = lustre.simple(init, update, render(view))
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
fn init(_flags) {
0
}
type Msg {
Incr
Decr
}
fn update(model, msg) {
case msg {
Incr -> model + 1
Decr -> model - 1
}
}
fn main_class() {
craft.class([
craft.background("red"),
craft.display("flex"),
craft.flex_direction("row"),
craft.gap(px(12)),
craft.padding(px(12)),
craft.hover([craft.background("yellow")]),
craft.media(media.max_width(px(450)), [
craft.background("purple"),
craft.hover([craft.background("white")]),
]),
])
|> craft.to_lustre()
}
fn color_class(model: Model) {
let back = case model % 3 {
0 -> "blue"
_ -> "green"
}
let id = "color-" <> back
craft.dynamic(id, [craft.background(back)])
|> craft.to_lustre()
}
fn view(model) {
let count = int.to_string(model)
div([main_class()], [
button([on_click(Incr)], [text(" + ")]),
p([color_class()], [text(count)]),
button([on_click(Decr)], [text(" - ")])
])
}
```

## Compiling static classes

Craft exposes a single function [`class`](#class) allowing you to build your
class. The first time your function is called, the corresponding styles will
be compiled into CSS rules, and pushed in your browser or your SSR stylesheet.
Every time you'll call the function in the future, no computation will be done,
the class name will be returned, thanks to memoization.
Craft exposes a single function [`class`](https://hexdocs.pm/craft/craft.html#class)
allowing you to build your class. The first time your function is called, the
corresponding styles will be compiled into CSS rules, and pushed in your browser
or your SSR stylesheet. Every time you'll call the function in the future, no
computation will be done, the class name will be returned, thanks to memoization.

```gleam
import craft
Expand All @@ -35,10 +133,10 @@ fn my_class() -> String {

## Compiling dynamic classes

Craft exposes another function [`variable`](#variable) allowing you to build a
dynamic class, changing over time. Each time the function is called, the
properties in the declaration will be compiled into CSS, the previous class
will be wiped from the browser, and the new one will pushed.
Craft exposes another function [`dynamic`](https://hexdocs.pm/craft/craft.html#dynamic)
allowing you to build a dynamic class, changing over time. Each time the function
is called, the properties in the declaration will be compiled into CSS, the previous
class will be wiped from the browser, and the new one will pushed.

```gleam
import craft
Expand All @@ -55,40 +153,6 @@ fn my_variable_class(is_column: Bool) -> String {
}
```

## Usage with Lustre

[Lustre](https://hexdocs.pm/lustre/) is the main framework for frontend
development in Gleam. Because of this, craft provides a function to directly
use classes in Lustre views: [`to_lustre()`](#to_lustre). Just use it in place
of [`to_class_name()`](#to_class_name) to get a Lustre attribute and use it
in your views.

```gleam
import craft
import lustre/element/html
// With a pipeline.
fn my_view() {
[craft.background("red")]
|> craft.class()
|> craft.to_lustre()
|> list.repeat(1)
|> html.div(_, [])
}
// With a variable class.
fn my_other_view(model: Bool) {
let color = case model {
True -> "red"
False -> "blue"
}
html.div(
[craft.to_lustre(craft.variable([craft.background(color)]))],
[],
)
}
```

## Using media queries and pseudo-selectors

Because we're in CSS-in-Gleam, we can leverage on the full CSS power,
Expand Down Expand Up @@ -140,16 +204,57 @@ The example above will be compiled to the following CSS.
}
```

## Usage with Lustre — Details

[Lustre](https://hexdocs.pm/lustre/) is the recommended framework for frontend
development in Gleam. Craft tries to simplify as much the development with Lustre.
That's why Craft exposes a [`lustre_setup`](https://hexdocs.pm/craft/craft.html#lustre_setup)
function. This function creates a cache, and returns a middleware for the view function.
It comes as a "hook" (lustre does not offcially supports hooks right now): it setups
the cache before the view, and render the stylesheet after the view has executed.
It tries to be side-effect free in the `view` in order to have a predictable render
in Lustre, and stick with the Elm architecture mindset.

Once setuped, you can use classes in your Lustre views: [`to_lustre()`](https://hexdocs.pm/craft/craft.html#to_lustre).
Just use it in place of [`to_class_name()`](https://hexdocs.pm/craft/craft.html#to_class_name)
to get a Lustre attribute and use it in your views.

```gleam
import craft
import lustre/element/html
// With a pipeline.
fn my_view() {
[craft.background("red")]
|> craft.class()
|> craft.to_lustre()
|> list.repeat(1)
|> html.div(_, [])
}
// With a variable class.
fn my_other_view(model: Bool) {
let color = case model {
True -> "red"
False -> "blue"
}
html.div(
[craft.to_lustre(craft.variable([craft.background(color)]))],
[],
)
}
```

## Some opinions on properties

A lot of properties are accessible directly through the `craft` package.
But with time, some could be added, and new features for existing features
can appear. That's why craft will never try to be on your way: at any time
you can access [`property()`](#property), which allows you to push any
arbitrary property in a class. Another thing is that craft will always let
you access raw, low-level properties. If you're trying to use something like
`craft.width("auto")` and the property does not support String, look for a
variant with an underscore (`_`), it should fullfill your needs, like
`craft.width_("auto")`!
you can access [`property()`](https://hexdocs.pm/craft/craft.html#property),
which allows you to push any arbitrary property in a class. Another thing is ç
that craft will always let you access raw, low-level properties. If you're
trying to use something like `craft.width("auto")` and the property does not
support String, look for a variant with an underscore (`_`), it should fullfill
your needs, like `craft.width_("auto")`!
In case something is missing or a property does not have its underscore
alternative, [open an issue — or better, a PR — on the repo!](https://github.com/ghivert/craft)
4 changes: 2 additions & 2 deletions e2e/stylesheet_render/src/stylesheet_render.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub type Msg {
pub fn main() {
let assert Ok(render) =
craft_options.node()
|> craft.setup()
|> craft.lustre_setup()

let assert Ok(_) =
fn(_) { 0 }
Expand Down Expand Up @@ -55,7 +55,7 @@ fn color_class(model: Model) {
_ -> "green"
}
let id = "color-" <> back
craft.variable(id, [craft.background(back)])
craft.dynamic(id, [craft.background(back)])
|> craft.to_lustre()
}

Expand Down
2 changes: 1 addition & 1 deletion manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages = [
{ name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" },
{ name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "lustre", version = "4.1.4", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "BA9ED993187B0BB721FFBB1F01F6CCA14548F68873C55567B77918D33D0D9ECB" },
{ name = "lustre", version = "4.1.5", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "C90B3DC868D346C49E98C8A62F2E594AE559D5FF25A46269D60FAA5939FCE827" },
{ name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
]

Expand Down
Loading

0 comments on commit d8ae736

Please sign in to comment.