summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--TODO.norg2
-rw-r--r--docs/README.md9
-rw-r--r--docs/api/functions.md266
-rw-r--r--docs/how-it-works.md130
-rw-r--r--docs/how_it_works.md4
-rw-r--r--docs/recipies.md6
-rw-r--r--examples/clock/style.css2
-rw-r--r--package.json1
-rw-r--r--src/eval.ts14
-rw-r--r--src/index.ts1
11 files changed, 400 insertions, 41 deletions
diff --git a/README.md b/README.md
index a27d56b..1b4c8e2 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,11 @@ A ui library where you only write CSS. No HTML, no JS, no build system, only CSS
> Disclaimer: Don't use this
-
## Usage
-### Docs
-WIP. Coming soon maybe?
+- [Read the documentation](https://github.com/phenax/css-everything/tree/main/docs/README.md) to become enlightened.
+- [Here's how this works](https://github.com/phenax/css-everything/tree/main/docs/how-it-works.md).
+
### Simple example
You can start by adding the script tag for the renderer inside the body
diff --git a/TODO.norg b/TODO.norg
index 588994e..37e5fe5 100644
--- a/TODO.norg
+++ b/TODO.norg
@@ -8,7 +8,7 @@
- (x) Scoped catch on try
- (x) `do` expression
- (x) `let` expression
- - ( ) `h` declarations
+ - (x) `h` declarations
- ( ) `has-class`
- ( ) `add-class` & `remove-class` should use self if id is not specified?
- ( ) Update `--cssx-text` on update
diff --git a/docs/README.md b/docs/README.md
index e69de29..0e2fb78 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -0,0 +1,9 @@
+# Documentation
+
+- [API/functions](./api/functions.md)
+- [API/hooks](./api/hooks.md)
+- [API/properties](./api/properties.md)
+- [How it works?](./how-it-works.md)
+- [Security](./security.md)
+- [Common recipies](./recipies.md)
+
diff --git a/docs/api/functions.md b/docs/api/functions.md
index 39f85cb..55fd701 100644
--- a/docs/api/functions.md
+++ b/docs/api/functions.md
@@ -5,19 +5,33 @@ type custom-property-name = `--${string}`
type selector = string // Any css selector or identifier (used as id)
type condition = string // empty string, 0, 'false', "'false'" and "\"false\"" are all false, the rest are fine. Don't ask.
type duration = number | `${number}ms` | `${number}s`
+type pair = string: any
```
-## get-var
-Get css custom property from an element
+===
+
+
+## Core
+
+### get-var
+Get css custom property from an element. Basically var but evaluated lazily.
NOTE: Avoid using `var` inside cssx expressions.
```typescript
function get-var(custom-property-name): string
-function get-var(selector, custom-property-name): string
+function get-var(custom-property-name, default-value): string
+```
+
+Example -
+```css
+#my-element {
+ --cssx-text: get-var(--some-variable);
+}
```
-## update
+
+#### update
Update a css custom property on an element
```typescript
@@ -25,24 +39,230 @@ function update(custom-property-name, string): void
function update(selector, custom-property-name, string): string
```
+Example -
+```css
+#my-element {
+ --cssx-on-click: update(some-el, --text, attr(input-element, 'value'));
-## js-eval
-Evaluate any js expression. Easy escape hatch into writing the worst code humanly possible.
+ --cssx-children: input#input-element #some-el;
+}
-```typescript
-function js-eval(string): string
+#some-el { --text: "default"; }
+#some-el::after { content: var(--text); }
```
-## if
+### if
If expression. You know how this one goes. If truthy, it'll pick the second argument, else the third.
```typescript
function if(condition, any, any): any
```
+Example -
+```css
+#my-element {
+ --boolean: false;
+
+ --cssx-on-update:
+ update(background-color,
+ if(
+ get-var(--boolean)
+ 'DarkGoldenRod',
+ 'PapayaWhip'
+ )
+ );
+}
+```
+
+### map
+A constructor function to create a map of key value pairs.
+
+```typescript
+function map(...pair): map
+```
+
+Example -
+```css
+#my-element {
+ --cssx-children: instance(
+ div#some-element,
+ map(--prop1: "hello", --prop2: "world")
+ );
+}
+```
+
+### seq
+> WIP Docs
+
+### call
+Call a "function". A function is any series of expressions defined in a css custom property.
+
+NOTE: Every function call creates a new dom node for computing the result. Dom nodes are the call stack.
+
+```typescript
+function call(var-identifier, map): any
+```
+
+Example -
+```css
+#my-element {
+ --factorial: if(
+ js-expr(string(get-var(--n), '> 1')),
+ js-expr(string(
+ get-var(--n),
+ ' * ',
+ call(--factorial, map(--n: js-expr(string(get-var(--n), ' - 1'))))
+ )),
+ 1
+ );
+
+ --cssx-on-mount: js-expr(string(
+ 'console.log("',
+ call(--factorial, map(--n: 5)),
+ '")'
+ ));
+}
+```
+
+Or let's just go nuts with functions.
+Because we use named properties as arguments and css is cascading, all named properties are implicitly available to everything getting called.
+
+> Not a design choice, a design consequence.
+
+So `--left` and `--right` is implicitly available to `--binary-op`.
+
+> NOTE: `func` in `--binary-op` doesn't do anything. It's to make the developer feel better.
+
+```css
+#my-element {
+ --binary-op:
+ func(--left, --op, --right)
+ js-expr(string(get-var(--left), get-var(--op), get-var(--right)));
+ --greater-than: call(--binary-op, map(--op: ' >= '));
+ --minus: call(--binary-op, map(--op: ' - '));
+ --multiply: call(--binary-op, map(--op: ' * '));
+
+ --factorial: if(call(--greater-than, map(--left: get-var(--n), --right: 1)),
+ call(--multiply, map(
+ --left: get-var(--n),
+ --right: call(--factorial,
+ map(--n: call(--minus,
+ map(--left: get-var(--n), --right: 1)))
+ )
+ )),
+ 1
+ );
+}
+```
+
+### string
+Concatenate strings together / Cast a value to string explicitly
+
+```typescript
+function string(...string): string
+```
+
+```css
+#my-element {
+ --log-stuff: 'Stuff to log to console';
+ --cssx-on-mount: js-expr(string('console.log("', get-var(--log-stuff), '")'));
+}
+```
-## delay
+### quotify
+Add quotes around a value
+
+```typescript
+function quotify(string): `'${string}'`
+```
+
+```css
+#my-element {
+ --log-stuff: 'Stuff to log to console';
+ --cssx-on-mount: js-expr(string('console.log(', quotify(get-var(--log-stuff)), ')'));
+}
+```
+
+
+### unquotify
+Remove quotes from a value. No example here, use your imagination.
+
+```typescript
+function quotify(string): string
+```
+
+
+### do
+Evaluate a series of expressions in sequence and return the last value.
+
+```css
+#my-element {
+ --cssx-on-mount:
+ if(get-var(--some-boolean),
+ do(
+ add-class(loading),
+ delay(1s),
+ remove-class(loading)
+ ),
+ noop(),
+ )
+ ;
+}
+```
+
+
+### try
+The standard try/catch as an expression. The error expression is scoped and gets access to a `--error` value.
+
+```css
+form#my-form {
+ --cssx-on-submit:
+ prevent-default()
+ add-class(form, 'submitting')
+ try(
+ do(
+ request('/your-api/some-api', 'POST'),
+ add-class(form, 'submitted')
+ ),
+ js-eval(string('alert("', get-var(--error), '")'))
+ )
+ remove-class(form, 'submitting')
+ ;
+}
+```
+
+### let
+Create a binding for use inside a scoped expression.
+
+`--random` is only available within the let binding
+```css
+#my-element {
+ --cssx-on-mount:
+ let(--random, js-eval('Math.random()'),
+ js-eval('alert("', get-var(--random),'")')
+ )
+ ;
+}
+```
+
+
+===
+
+
+## Others
+
+### js-eval
+Evaluate any js expression. Easy escape hatch directly to hell.
+
+```typescript
+function js-eval(string): string
+```
+
+### request
+> WIP Docs
+
+### delay
Wait a bit.
```typescript
@@ -56,36 +276,30 @@ Examples for input -
- `delay(0.5s)`: wait for 500 milliseconds
-## load-cssx
-
-
-## set-attr
-
-
-## attr
-
+===
-## prevent-default
+## DOM
-## request
+### load-cssx
+Load more of this abomination into your page
-## add-children
+### set-attr
-## remove-element
+### attr
-## call-method
+### add-children
-## map
+### remove-element
-## call
+### prevent-default
-## func
+### call-method
diff --git a/docs/how-it-works.md b/docs/how-it-works.md
new file mode 100644
index 0000000..c4d6fe3
--- /dev/null
+++ b/docs/how-it-works.md
@@ -0,0 +1,130 @@
+# 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.
+
+> NOTE: The styles for `#my-element` has to be loaded into the dom before the
+
+
+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)
+ if(
+ js-expr(string(get-var(--n), '> 1')),
+ js-expr(string(
+ get-var(--n),
+ ' * ',
+ call(--factorial, map(--n: js-expr(string(get-var(--n), ' - 1'))))
+ )),
+ 1
+ );
+
+ --cssx-on-mount: js-expr(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.
+
+The way this works is that it creates a new dom element 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 immedietely deleted as soon as the required computation is completed.
+
+> 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 to slow things down to debug.
+
+
diff --git a/docs/how_it_works.md b/docs/how_it_works.md
deleted file mode 100644
index 6329723..0000000
--- a/docs/how_it_works.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# How does it work?
-
-WIP
-
diff --git a/docs/recipies.md b/docs/recipies.md
index 52dddf4..133f69f 100644
--- a/docs/recipies.md
+++ b/docs/recipies.md
@@ -95,3 +95,9 @@ NOTE: Try to avoid doing this. Please refer to [./security.md](./security.md) fo
}
```
+
+## Debugging call stack
+
+> WIP docs
+
+
diff --git a/examples/clock/style.css b/examples/clock/style.css
index 07b8cc9..110d95b 100644
--- a/examples/clock/style.css
+++ b/examples/clock/style.css
@@ -84,8 +84,8 @@ body * { box-sizing: border-box; }
top: 50%;
left: 50%;
transform-origin: 0% 50%;
- transform: rotate(0deg);
border-radius: 5px;
+ transform: rotate(0deg);
--get-transform: string('rotate(', get-var(--angle), 'deg)');
diff --git a/package.json b/package.json
index f8c62c5..2c5d06a 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"format": "prettier --write './{src,tests}/**/*.{ts,html,css}'",
"lint": "eslint ./src",
"fix": "yarn lint --fix && yarn format",
+ "pub:patch": "yarn lint && yarn build && yarn publish --access=public --patch",
"test": "jest"
},
"devDependencies": {
diff --git a/src/eval.ts b/src/eval.ts
index 9a55828..636023b 100644
--- a/src/eval.ts
+++ b/src/eval.ts
@@ -82,11 +82,11 @@ export const evalExpr = async (
}
const QUOTE_REGEX = /^['"](.*)(?=['"]$)['"]$/g
-const dequotify = (s: string) => s.replace(QUOTE_REGEX, '$1')
+const unquotify = (s: string) => s.replace(QUOTE_REGEX, '$1')
export const evalValueToString = (val: EvalValue): string | undefined =>
match<string | undefined, EvalValue>(val, {
- String: s => dequotify(s),
+ String: s => unquotify(s),
Boolean: b => `${b}`,
Number: n => `${n}`,
VarIdentifier: s => s,
@@ -105,7 +105,7 @@ const evalValueToNumber = (val: EvalValue): number | undefined =>
const evalValueToBoolean = (val: EvalValue): boolean =>
match<boolean, EvalValue>(val, {
- String: s => !['false', '', '0'].includes(dequotify(s)),
+ String: s => !['false', '', '0'].includes(unquotify(s)),
Boolean: b => b,
Number: n => !!n,
Value: v => !!v,
@@ -282,6 +282,10 @@ const getFunctions = (
seq: async () => EvalValue.Lazy(args),
+ // noop
+ noop: async () => EvalValue.Void(),
+ func: async () => EvalValue.Void(),
+
call: async () => {
const varId = match<string | undefined, EvalValue>(
await evalExpr(args[0], actions),
@@ -321,9 +325,9 @@ const getFunctions = (
const str = await evalExprAsString(args[0], actions)
return EvalValue.String(`'${str || ''}'`)
},
- dequotify: async () => {
+ unquotify: async () => {
const str = await evalExprAsString(args[0], actions)
- return EvalValue.String(dequotify(str || ''))
+ return EvalValue.String(unquotify(str || ''))
},
try: async () => {
diff --git a/src/index.ts b/src/index.ts
index bc471ee..57ef6d0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -296,7 +296,6 @@ const createLayer = async (
$childrenRoot,
)
$childrenRoot.appendChild($child)
- console.log($child.dataset.element, isNewElement, declaration)
await manageElement($child, isNewElement)
if (declaration.children.length > 0) {