diff --git a/build/lib/highlighter.js b/build/lib/highlighter.js
index ce1b0d63..2ce6336d 100644
--- a/build/lib/highlighter.js
+++ b/build/lib/highlighter.js
@@ -16,6 +16,9 @@ const highlighter = await createHighlighter({
"vue",
"marko",
],
+ langAlias: {
+ ripple: "jsx", // until ripple is supported by shiki
+ },
});
const md = MarkdownIt({
diff --git a/content/1-reactivity/1-declare-state/ripple/Name.ripple b/content/1-reactivity/1-declare-state/ripple/Name.ripple
new file mode 100644
index 00000000..d482e79d
--- /dev/null
+++ b/content/1-reactivity/1-declare-state/ripple/Name.ripple
@@ -0,0 +1,7 @@
+import { track } from "ripple";
+
+export component Name() {
+ let name = track("John");
+
+
{`Hello ${@name}`}
+}
\ No newline at end of file
diff --git a/content/1-reactivity/2-update-state/ripple/Name.ripple b/content/1-reactivity/2-update-state/ripple/Name.ripple
new file mode 100644
index 00000000..a96596eb
--- /dev/null
+++ b/content/1-reactivity/2-update-state/ripple/Name.ripple
@@ -0,0 +1,8 @@
+import { track } from "ripple";
+
+export component Name() {
+ let name = track("John");
+ @name = "Jane";
+
+ {`Hello ${@name}`}
+}
\ No newline at end of file
diff --git a/content/1-reactivity/3-computed-state/ripple/DoubleCount.ripple b/content/1-reactivity/3-computed-state/ripple/DoubleCount.ripple
new file mode 100644
index 00000000..955a1c29
--- /dev/null
+++ b/content/1-reactivity/3-computed-state/ripple/DoubleCount.ripple
@@ -0,0 +1,8 @@
+import { track } from "ripple";
+
+export component DoubleCount() {
+ let count = track(10);
+ const doubleCount = track(() => @count * 2);
+
+ {@doubleCount}
+}
\ No newline at end of file
diff --git a/content/2-templating/1-minimal-template/ripple/HelloWorld.ripple b/content/2-templating/1-minimal-template/ripple/HelloWorld.ripple
new file mode 100644
index 00000000..e3984cbe
--- /dev/null
+++ b/content/2-templating/1-minimal-template/ripple/HelloWorld.ripple
@@ -0,0 +1,3 @@
+export component HelloWorld() {
+ {"Hello world"}
+}
\ No newline at end of file
diff --git a/content/2-templating/2-styling/ripple/CssStyle.ripple b/content/2-templating/2-styling/ripple/CssStyle.ripple
new file mode 100644
index 00000000..d3fddf4a
--- /dev/null
+++ b/content/2-templating/2-styling/ripple/CssStyle.ripple
@@ -0,0 +1,10 @@
+export component CssStyle() {
+ {"I am red"}
+
+
+
+}
\ No newline at end of file
diff --git a/content/2-templating/3-loop/ripple/Colors.ripple b/content/2-templating/3-loop/ripple/Colors.ripple
new file mode 100644
index 00000000..7226018e
--- /dev/null
+++ b/content/2-templating/3-loop/ripple/Colors.ripple
@@ -0,0 +1,9 @@
+export component Colors() {
+ const colors = ["red", "green", "blue"];
+
+
+ for (const color of colors) {
+ - {color}
+ }
+
+}
\ No newline at end of file
diff --git a/content/2-templating/4-event-click/ripple/Counter.ripple b/content/2-templating/4-event-click/ripple/Counter.ripple
new file mode 100644
index 00000000..c52621f7
--- /dev/null
+++ b/content/2-templating/4-event-click/ripple/Counter.ripple
@@ -0,0 +1,10 @@
+export component Counter() {
+ let $count = 0;
+
+ function incrementCount() {
+ $count++;
+ }
+
+ {`Count: ${$count}`}
+
+}
\ No newline at end of file
diff --git a/content/2-templating/5-dom-ref/ripple/InputFocused.ripple b/content/2-templating/5-dom-ref/ripple/InputFocused.ripple
new file mode 100644
index 00000000..7289c4ca
--- /dev/null
+++ b/content/2-templating/5-dom-ref/ripple/InputFocused.ripple
@@ -0,0 +1,7 @@
+export component InputFocused() {
+ function autofocus(element) {
+ element.focus();
+ }
+
+
+}
\ No newline at end of file
diff --git a/content/2-templating/6-conditional/ripple/TrafficLight.ripple b/content/2-templating/6-conditional/ripple/TrafficLight.ripple
new file mode 100644
index 00000000..d91958bb
--- /dev/null
+++ b/content/2-templating/6-conditional/ripple/TrafficLight.ripple
@@ -0,0 +1,26 @@
+import { track } from "ripple";
+
+const TRAFFIC_LIGHTS = ["red", "orange", "green"];
+
+export component TrafficLight() {
+ let lightIndex = track(0);
+
+ const light = track(() => TRAFFIC_LIGHTS[@lightIndex]);
+
+ function nextLight() {
+ @lightIndex = (@lightIndex + 1) % TRAFFIC_LIGHTS.length;
+ }
+
+
+ {`Light is ${@light}`}
+
+ {"You must "}
+ if (@light === "red") {
+ {"STOP"}
+ } else if (@light === "orange") {
+ {"SLOW DOWN"}
+ } else if (@light === "green") {
+ {"GO"}
+ }
+
+}
\ No newline at end of file
diff --git a/content/3-lifecycle/1-on-mount/ripple/PageTitle.ripple b/content/3-lifecycle/1-on-mount/ripple/PageTitle.ripple
new file mode 100644
index 00000000..6f58135d
--- /dev/null
+++ b/content/3-lifecycle/1-on-mount/ripple/PageTitle.ripple
@@ -0,0 +1,11 @@
+import { effect } from "ripple";
+
+export component PageTitle() {
+ let $pageTitle = "";
+
+ effect(() => {
+ $pageTitle = document.title;
+ });
+
+ {`Page title: ${$pageTitle}`}
+}
\ No newline at end of file
diff --git a/content/3-lifecycle/2-on-unmount/ripple/Time.ripple b/content/3-lifecycle/2-on-unmount/ripple/Time.ripple
new file mode 100644
index 00000000..4e66d9c0
--- /dev/null
+++ b/content/3-lifecycle/2-on-unmount/ripple/Time.ripple
@@ -0,0 +1,15 @@
+import { effect } from "ripple";
+
+export component Time() {
+ let $time = new Date().toLocaleTimeString();
+
+ effect(() => {
+ const timer = setInterval(() => {
+ $time = new Date().toLocaleTimeString();
+ }, 1000);
+
+ return () => clearInterval(timer);
+ });
+
+ {`Current time: ${$time}`}
+}
\ No newline at end of file
diff --git a/content/4-component-composition/1-props/ripple/App.ripple b/content/4-component-composition/1-props/ripple/App.ripple
new file mode 100644
index 00000000..a124bae0
--- /dev/null
+++ b/content/4-component-composition/1-props/ripple/App.ripple
@@ -0,0 +1,10 @@
+import { UserProfile } from "./UserProfile.ripple";
+
+export component App() {
+
+}
\ No newline at end of file
diff --git a/content/4-component-composition/1-props/ripple/UserProfile.ripple b/content/4-component-composition/1-props/ripple/UserProfile.ripple
new file mode 100644
index 00000000..8e30a867
--- /dev/null
+++ b/content/4-component-composition/1-props/ripple/UserProfile.ripple
@@ -0,0 +1,11 @@
+export component UserProfile({
+ name = "",
+ age = null,
+ favouriteColors = [],
+ isAvailable = false
+}) {
+ {`My name is ${name}!`}
+ {`My age is ${age}!`}
+ {`My favourite colors are ${favouriteColors.join(", ")}!`}
+ {`I am ${isAvailable ? "available" : "not available"}`}
+}
\ No newline at end of file
diff --git a/content/4-component-composition/2-emit-to-parent/ripple/AnswerButton.ripple b/content/4-component-composition/2-emit-to-parent/ripple/AnswerButton.ripple
new file mode 100644
index 00000000..60ae0705
--- /dev/null
+++ b/content/4-component-composition/2-emit-to-parent/ripple/AnswerButton.ripple
@@ -0,0 +1,5 @@
+export component AnswerButton({ onYes, onNo }) {
+
+
+
+}
\ No newline at end of file
diff --git a/content/4-component-composition/2-emit-to-parent/ripple/App.ripple b/content/4-component-composition/2-emit-to-parent/ripple/App.ripple
new file mode 100644
index 00000000..09747228
--- /dev/null
+++ b/content/4-component-composition/2-emit-to-parent/ripple/App.ripple
@@ -0,0 +1,19 @@
+
+import { track } from "ripple";
+import { AnswerButton } from "./AnswerButton.ripple";
+
+export component App() {
+ let isHappy = track(true);
+
+ function onAnswerNo() {
+ @isHappy = false;
+ }
+
+ function onAnswerYes() {
+ @isHappy = true;
+ }
+
+ {`Are you happy?`}
+
+ {@isHappy ? "😀" : "😥"}
+}
\ No newline at end of file
diff --git a/content/4-component-composition/3-slot/ripple/App.ripple b/content/4-component-composition/3-slot/ripple/App.ripple
new file mode 100644
index 00000000..9b652a33
--- /dev/null
+++ b/content/4-component-composition/3-slot/ripple/App.ripple
@@ -0,0 +1,5 @@
+import { FunnyButton } from "./FunnyButton.ripple";
+
+export component App() {
+ {"Click me!"}
+}
\ No newline at end of file
diff --git a/content/4-component-composition/3-slot/ripple/FunnyButton.ripple b/content/4-component-composition/3-slot/ripple/FunnyButton.ripple
new file mode 100644
index 00000000..b8840b41
--- /dev/null
+++ b/content/4-component-composition/3-slot/ripple/FunnyButton.ripple
@@ -0,0 +1,7 @@
+export component FunnyButton({ children }) {
+
+}
\ No newline at end of file
diff --git a/content/4-component-composition/4-slot-fallback/ripple/App.ripple b/content/4-component-composition/4-slot-fallback/ripple/App.ripple
new file mode 100644
index 00000000..685ebbed
--- /dev/null
+++ b/content/4-component-composition/4-slot-fallback/ripple/App.ripple
@@ -0,0 +1,6 @@
+import { FunnyButton } from "./FunnyButton.ripple";
+
+export component App() {
+
+ {"I got content!"}
+}
\ No newline at end of file
diff --git a/content/4-component-composition/4-slot-fallback/ripple/FunnyButton.ripple b/content/4-component-composition/4-slot-fallback/ripple/FunnyButton.ripple
new file mode 100644
index 00000000..72d3ea8a
--- /dev/null
+++ b/content/4-component-composition/4-slot-fallback/ripple/FunnyButton.ripple
@@ -0,0 +1,11 @@
+export component FunnyButton({ children }) {
+
+}
\ No newline at end of file
diff --git a/content/6-form-input/1-input-text/ripple/InputHello.ripple b/content/6-form-input/1-input-text/ripple/InputHello.ripple
new file mode 100644
index 00000000..3d331307
--- /dev/null
+++ b/content/6-form-input/1-input-text/ripple/InputHello.ripple
@@ -0,0 +1,12 @@
+import { track } from "ripple";
+
+export component InputHello() {
+ let text = track("Hello world");
+
+ function handleChange(event) {
+ @text = event.target.value;
+ }
+
+ {@text}
+
+}
\ No newline at end of file
diff --git a/content/6-form-input/2-checkbox/ripple/IsAvailable.ripple b/content/6-form-input/2-checkbox/ripple/IsAvailable.ripple
new file mode 100644
index 00000000..0a18fb78
--- /dev/null
+++ b/content/6-form-input/2-checkbox/ripple/IsAvailable.ripple
@@ -0,0 +1,17 @@
+import { track } from "ripple";
+
+export component IsAvailable() {
+ let isAvailable = track(false);
+
+ function handleChange() {
+ @isAvailable = !@isAvailable;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/content/6-form-input/3-radio/ripple/PickPill.ripple b/content/6-form-input/3-radio/ripple/PickPill.ripple
new file mode 100644
index 00000000..6fc436d8
--- /dev/null
+++ b/content/6-form-input/3-radio/ripple/PickPill.ripple
@@ -0,0 +1,29 @@
+import { track } from "ripple";
+
+export component PickPill() {
+ let picked = track("red");
+
+ function handleChange(event) {
+ @picked = event.target.value;
+ }
+
+ {`Picked: ${@picked}`}
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/content/6-form-input/4-select/ripple/ColorSelect.ripple b/content/6-form-input/4-select/ripple/ColorSelect.ripple
new file mode 100644
index 00000000..7edd320f
--- /dev/null
+++ b/content/6-form-input/4-select/ripple/ColorSelect.ripple
@@ -0,0 +1,24 @@
+import { track } from "ripple";
+
+const colors = [
+ { id: 1, text: "red" },
+ { id: 2, text: "blue" },
+ { id: 3, text: "green" },
+ { id: 4, text: "gray", isDisabled: true },
+];
+
+export component ColorSelect() {
+ let selectedColorId = track(2);
+
+ function handleChange(event) {
+ @selectedColorId = event.target.value;
+ }
+
+
+}
\ No newline at end of file
diff --git a/content/7-webapp-features/1-render-app/ripple/App.ripple b/content/7-webapp-features/1-render-app/ripple/App.ripple
new file mode 100644
index 00000000..f6ba9b83
--- /dev/null
+++ b/content/7-webapp-features/1-render-app/ripple/App.ripple
@@ -0,0 +1,3 @@
+export component App() {
+ {"Hello world"}
+}
\ No newline at end of file
diff --git a/content/7-webapp-features/1-render-app/ripple/index.html b/content/7-webapp-features/1-render-app/ripple/index.html
new file mode 100644
index 00000000..7c7079ad
--- /dev/null
+++ b/content/7-webapp-features/1-render-app/ripple/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/content/7-webapp-features/1-render-app/ripple/main.js b/content/7-webapp-features/1-render-app/ripple/main.js
new file mode 100644
index 00000000..9fd423a0
--- /dev/null
+++ b/content/7-webapp-features/1-render-app/ripple/main.js
@@ -0,0 +1,6 @@
+import { mount } from "ripple";
+import { App } from "./App.ripple";
+
+mount(App, {
+ target: document.getElementById("app"),
+});
diff --git a/frameworks.mjs b/frameworks.mjs
index d78f33bf..962485fb 100644
--- a/frameworks.mjs
+++ b/frameworks.mjs
@@ -475,6 +475,23 @@ const frameworks = [
repositoryLink: "https://github.com/aurelia/framework",
mainPackageName: "aurelia-framework",
},
+ {
+ id: "ripple",
+ title: "Ripple",
+ frameworkName: "Ripple",
+ isCurrentVersion: true,
+ img: "framework/ripple.svg",
+ eslint: {
+ files: ["!**"],
+ },
+ playgroundURL: "https://www.ripplejs.com/playground",
+ documentationURL: "https://www.ripplejs.com/",
+ filesSorter(files) {
+ return sortAllFilenames(files, ["index.html", "main.js", "App.ripple"]);
+ },
+ repositoryLink: "https://github.com/trueadm/ripple",
+ mainPackageName: "ripple",
+ },
];
export function matchFrameworkId(id) {
diff --git a/public/framework/ripple.svg b/public/framework/ripple.svg
new file mode 100644
index 00000000..9d313cb1
--- /dev/null
+++ b/public/framework/ripple.svg
@@ -0,0 +1,5 @@
+