Skip to main content

Ripple

Ripple is a TypeScript UI framework that takes the best parts of React, Solid and Svelte and combines them into one package.

Ripple components can be used in LiveCodes as documented in the Ripple docs. See below for usage.

Demo

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"activeEditor": "script",
"script": {
"language": "ripple",
"content": "import { track } from 'ripple';\n\nexport default component Counter() {\n let count = track(0);\n let doubled = track(() => @count * 2);\n\n <div class='container'>\n <h2>{'Counter'}</h2>\n <p>{`Count: ${@count}`}</p>\n <p>{`Doubled: ${@doubled}`}</p>\n\n <button onClick={() => @count++}>{'Increment'}</button>\n <button onClick={() => @count = 0}>{'Reset'} </button>\n </div>\n\n <style>\n .container {\n text-align: center;\n font-family: \"Arial\", sans-serif;\n }\n\n button {\n font-size: 1em;\n padding: 6px 12px;\n margin: 0 6px;\n cursor: pointer;\n border: none;\n font-family: \"Courier New\", monospace;\n }\n\n button:hover {\n background-color: #e0e0e0;\n }\n </style>\n}"
}
}
};
createPlayground('#container', options);

Mounting

Auto-rendering

A component is mounted and rendered automatically as a Ripple component (without having to manually mount it) if the following conditions are met:

Root Element

To mount the application instance to a specific DOM element use "livecodes-app" as the element id in the HTML. Otherwise, if that element is not found, a new div element is added to document.body and is used to mount the instance. In case you need more control, you can manually mount the instance.

Example:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"markup": {
"language": "html",
"content": "<h1>Custom Root Element</h1>\n<div id=\"livecodes-app\"></div>\n<div>...other page content</div>\n"
},
"script": {
"language": "ripple",
"content": "import { track } from 'ripple';\n\nexport default component App() {\n let name = track('Ripple');\n <div>{`I'm a ${@name} component`}</div>\n <style>\n div {\n color: #0e5ca3;\n margin-bottom: 1em;\n }\n </style>\n}\n"
}
}
};
createPlayground('#container', options);

Manual Mount

Exports from Ripple code can be imported in markup editor from "./Component.ripple" and used to mount the application instance manually. See Exports for details.

Example:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"markup": {
"language": "html",
"content": "<h1>Manual Mount</h1>\n<div id=\"root\"></div>\n<div>...other page content</div>\n\n<script type=\"module\">\n import { mount } from 'ripple';\n import { App } from \"./Component.ripple\";\n\n mount(App, {\n props: { name: 'Ripple' },\n target: document.getElementById('root'),\n });\n</script>\n"
},
"script": {
"language": "ripple",
"content": "export component App(props: { name: string }) {\n <div>{`I'm a ${props.name} component`}</div>\n <style>\n div {\n color: #0e5ca3;\n margin-bottom: 1em;\n }\n </style>\n}\n"
}
}
};
createPlayground('#container', options);

Disabling Auto-rendering

To disable auto-rendering, set the custom settings disableAutoRender property to true.

Custom Settings
{
"ripple": {
"disableAutoRender": true
}
}

Importing Modules

NPM Modules

npm modules can be imported as described in the section about module resolution, including bare module imports and importing from different CDNs. Stylesheets imported in the script block are added as <link rel="stylesheet"> tags in the page head (see below).

Module imports can be customized using import maps as described in module resolution documentations.

Example:

show code
import { createPlayground } from 'livecodes';

const options = {
"params": {
"ripple": "import { track, effect } from \"ripple\";\nimport confetti from \"canvas-confetti\";\n\nexport default component App() {\n <div class=\"container\">\n let count = track(0);\n\n effect(() => {\n if (@count === 2) confetti();\n });\n\n <button onClick={() => @count++}>{@count}</button>\n\n if (@count > 1) {\n <div>{'Greater than 1!'}</div>\n }\n </div>\n\n <style>\n button {\n padding: 1rem;\n font-size: 1rem;\n cursor: pointer;\n }\n </style>\n}\n"
},
"formatCode": false
};
createPlayground('#container', options);

External Components

External Ripple components can be imported. The import URL can be either:

  • An absolute URL ending with .ripple extension.
  • A bare module import for a published npm module with full path to the component with extension.
  • A data URL starting with data:text/ripple.

Any bare or relative imports in the imported files are resolved and compiled recursively. If a processor is enabled (e.g. Tailwind CSS), classes in the imported files are detected and used in the generated CSS (see CSS Frameworks section below).

Example: (source)

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"activeEditor": "script",
"script": {
"language": "ripple",
"content": "// import from npm modules\nimport { track } from 'ripple';\nimport { Counter } from 'ripple-tailwind-counter-demo/src/Counter.ripple';\n// alternatively use absolute URLs\n// import { Counter } from 'https://raw.githubusercontent.com/hatemhosny/ripple-tailwind-counter-demo/refs/heads/main/src/Counter.ripple';\n\nexport default component App() {\n let name = track(\"World\");\n\n setTimeout(() => {\n @name = \"Ripple + Tailwind!\";\n }, 2000);\n\n <Counter {name} />\n}\n"
},
"processors": [
"tailwindcss"
]
}
};
createPlayground('#container', options);

Exports

Values exported from script editor (default or named) can be imported in the markup editor by importing from "./script" (with no extension).

This can be useful, for example, for manually mounting components in the markup editor.

note

When values are imported from "./script", auto-rendering is disabled, because it is assumed that you want to take control over component rendering.

Styles

CSS can be applied to the component using various ways:

Component Styles

Styles in a style tag in a Ripple component are applied to that component only.

CSS processors (e.g. SCSS, Stylus) can be used by specifying a lang attribute for the style tag.

Example:

Style Editor

Styles added in the style editor are applied globally to the result page. This can use different languages/processors supported in LiveCodes including CSS, SCSS, Less, Stylus, ..etc. See style documentation for more details.

And of course, styles and stylesheets added in markup editor are also applied globally.

Importing Stylesheets

Stylesheets imported in script editor are added as <link rel="stylesheet"> tags in the page head. The stylesheet URL can be an absolute URL or a path in the npm package. The URL has to end with ".css".

example:

CSS Modules

CSS modules are supported and are documented separately. Make sure to enable CSS modules (from style editor menu or in processors property of configuration object).

Demo:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"activeEditor": "script",
"style": {
"language": "css",
"content": ".title {\n color: green;\n font-family: sans-serif;\n}\n"
},
"script": {
"language": "ripple",
"content": "import classes from './style.module.css';\n\nexport default component() {\n <h1 class={classes.title}>\n {'Hello, CSS Modules!'}\n </h1>\n}\n"
},
"processors": [
"cssmodules"
]
}
};
createPlayground('#container', options);

CSS Frameworks

CSS Frameworks supported in LiveCodes (e.g. Tailwind CSS, UnoCSS, WindiCSS) can detect class names added in Ripple components. Make sure that the required utility is enabled (from style editor menu or in processors property of configuration object).

Example:

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"activeEditor": "script",
"script": {
"language": "ripple",
"content": "import { track } from 'ripple';\n\nexport default component Counter() {\n let count = track(0);\n\n <div class=\"flex flex-col items-center justify-center min-h-screen bg-gray-100\">\n <h1 class=\"header\">{'Ripple + Tailwind CSS'}</h1>\n <div class=\"flex items-center space-x-4 mb-4\">\n <button\n onClick={() => @count--}\n class=\"w-12 px-4 py-2 bg-red-500 text-white rounded-lg shadow hover:bg-red-600 transition cursor-pointer\"\n >\n {'-'}\n </button>\n <span class=\"text-3xl font-semibold text-gray-800\">{@count}</span>\n <button\n onClick={() => @count++}\n class=\"w-12 px-4 py-2 bg-green-500 text-white rounded-lg shadow hover:bg-green-600 transition cursor-pointer\"\n >\n {'+'}\n </button>\n </div>\n <button\n onClick={() => @count = 0}\n class=\"px-6 py-2 bg-blue-500 text-white rounded-lg shadow hover:bg-blue-600 transition cursor-pointer\"\n >\n {'Reset'}\n </button>\n </div>\n\n <style>\n .header {\n @apply text-2xl font-bold text-gray-800 mb-4;\n }\n </style>\n}\n"
},
"processors": [
"tailwindcss"
]
}
};
createPlayground('#container', options);

Language Info

Name

ripple

Extensions

.ripple

Editor

script

Compiler

The official Ripple compiler.

Version

ripple: v0.2.15

Code Formatting

Using Prettier.

Custom Settings

Custom settings can be added to the property ripple. These include:

  • "disableAutoRender" - disables auto-rendering (default: false).
  • "version" - specifies the version of Ripple compiler (default: "latest").

Example:

Custom Settings
{
"ripple": {
"version": "0.2.15"
}
}

Please note that custom settings should be valid JSON (i.e. functions are not allowed).

Starter Template

https://livecodes.io/?template=ripple

show code
import { createPlayground } from 'livecodes';

const options = {
"template": "ripple"
};
createPlayground('#container', options);