aboutsummaryrefslogtreecommitdiff
path: root/docs/how-it-works.md
blob: 0edacc236a76e1f4b7fa8936c2fb91a41188fd9d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# How does it work?
Who knows really? It's just magic for the most part.

===

## Creating the dom tree

Everything starts with the `body` element (by default).

In your css, you can use `body` or `html` or `:root`. As long as your root (body by default) inherits that property, it's all good.
```css
:root {
  --cssx-children: div#my-element;
}
```

This will create a div inside `body` with the `id` (and `data-element` attribute) of `my-element`.


Let's go deeper...

```css
:root {
  --cssx-children: div#my-element;
}

#my-element {
  --cssx-children: header#div-a main#div-b;
}
```

Now `my-element`, gets 2 children. You can probably figure out what those would look like.

You may have already noticed a problem here. If you don't override the --cssx-children property, wouldn't all children of body get access to that?

Well yeah, which is why, we have a `.cssx-layer` element between the parent and children. This element wraps all children and makes it so all the cssx property are unset. This can occasionally make styling a bit difficult but that's a YOU problem for trying to use this.


## Instances
Instances are sort of like components. You can instantiate elements and provide them some custom properties.

NOTE: Instances get unique ids so instances and children of instances cannot not use the id selector for the definition.

```css
#my-element {
  --cssx-children:
    instance(div#user-card, map(--name: "Sugom Afart", --age: 20))
    instance(div#user-card, map(--name: "Leeki Bahol", --age: 69))
    instance(div#user-card, map(--name: "Yamam Aho", --age: 40))
  ;
}

[data-instance=user-card] {
  --name: "default name";
  --age: 0;

  --cssx-children: div#name div#age;
}

[data-element=name]::after {
  /* Using the ::after element to set content via css */
  content: "Name: " var(--name);
}
[data-element=age] {
  /* Using the --cssx-text property because css doesn't like numbers in `content` */
  --cssx-text: string("Age: ", get-var(--age));
}
```



## Custom functions

This is by far the most "fun" aspect of this project. Take a look at the docs for [call](./api/functions.md#call) for the api and examples.

```css
#my-element {
  --factorial: func(--n: number)
    if(lte(get-var(--n), 1), 1,
      calc(
        get-var(--n)
        * call(--factorial, map(--n: calc(get-var(--n) - 1)))
      ));

  --cssx-on-mount: js-eval(string(
    'console.log("',
      call(--factorial, map(--n: 5)),
    '")'
  ));
}
```

> NOTE: `func` is noop and just exists for documentation. You can also do `func(--a: string, --b: number)` and it'll be valid syntax but ignored at evaluation. So basically, typescript.

> NOTE: If you come at me with how using `js-eval` is cheating, I won't be responsible for your injuries. JS-in-CSS is the future.

The way this works is that it creates a new dom element with `display: none` inside the caller (`#my-element`), which then becomes the scope for the function.
Whatever arguments are passed to `call` will be added as css properties to this dom element.
Then the expressions inside the function is evaluated within the context of that element.

This means that with `call(--factorial, map(--n: 5))` the dom tree would look something like this.

```html
<div id="my-element">
  <div class="cssx-layer"></div> <!-- This is where the children would go... if you had any, you virgin -->

  <div style="display: none; --n: 5;">
    <div style="display: none; --n: 4;">
      <div style="display: none; --n: 3;">
        <div style="display: none; --n: 2;">
          <div style="display: none; --n: 1;">
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
```

This is the call stack. This is deleted as soon as the required computation is completed. (tail-call optimization, maybe? Hahahahaha. Kill me.)

> PRO TIP 1: If you want the tree to persist even after the function is evaluated (for debugging), add the `data-debug-stack` attribute to the caller element

> PRO TIP 2: You could style these nodes to have this tree show up in the ui and use the `--cssx-text` property to display the arguments for each recursive function call

> PRO TIP 3: If you're running into infinite loops, good luck. Also, you can add `delay(1s)` at the start of the function to slow things down to debug.