diff --git a/README.md b/README.md
index f8973fc..93a29d6 100755
--- a/README.md
+++ b/README.md
@@ -39,3 +39,25 @@ Interactive Site Components manageed by pure CSS.
Hallo,Welt,TypeScript,WebComponent
```
+
+
+### Scrollspy
+
+Create list of
elements with data-scrollspy-name attribute. The name is used to find the corresponding element.
+
+```html
+
+
+
+
+Title
+```
+
+
+### Scroll-to-top
+
+```html
+
+```
diff --git a/package.json b/package.json
index 91fe2fb..0f5639a 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@leuffen/liscom",
- "version": "2.0.9",
+ "version": "2.0.10",
"description": "",
"main": "./dist/index.js",
"module": "./dist/index.js",
diff --git a/src/index.scss b/src/index.scss
index 5aea551..cab34a7 100755
--- a/src/index.scss
+++ b/src/index.scss
@@ -2,3 +2,4 @@
@import "slideshow/slideshow";
@import "typewriter-element/typewriter-element";
@import "slider/liscom-slider";
+@import "scroll-to-top/scroll-to-top";
diff --git a/src/index.ts b/src/index.ts
index 54e11ca..7254de1 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -26,3 +26,5 @@ import "./slideshow/slideshow";
import "./details-title/details-title";
import "./typewriter-element/typewriter-element";
import "./slider/liscom-slider";
+import "./scrollspy/scrollspy";
+import "./scroll-to-top/scroll-to-top";
diff --git a/src/scroll-to-top/scroll-to-top.scss b/src/scroll-to-top/scroll-to-top.scss
new file mode 100644
index 0000000..08ddb16
--- /dev/null
+++ b/src/scroll-to-top/scroll-to-top.scss
@@ -0,0 +1,35 @@
+liscom-scroll-to-top {
+ opacity: 0;
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ z-index: 99;
+ border: none;
+ outline: none;
+ background-color: rgba(0, 0, 0, 0.1);
+ box-shadow: rgba(0,0,0,0.2) 0px 0px 5px;
+ color: white;
+ cursor: pointer;
+ padding: 10px;
+ border-radius: 4px;
+
+ display: flex;
+ align-content: center;
+ align-items: center;
+ text-align: center;
+
+ justify-content: center;
+ width: 2.8rem;
+ height: 2.8rem;
+ transition: opacity 0.5s ease-in-out, background-color 0.2s ease-in-out;
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.2);
+ }
+ &.show {
+ opacity: 1;
+ }
+ * {
+ color: white;
+ }
+}
+
diff --git a/src/scroll-to-top/scroll-to-top.ts b/src/scroll-to-top/scroll-to-top.ts
new file mode 100644
index 0000000..b8570f4
--- /dev/null
+++ b/src/scroll-to-top/scroll-to-top.ts
@@ -0,0 +1,30 @@
+import {customElement} from "@kasimirjs/embed";
+
+
+@customElement("liscom-scroll-to-top")
+class ScrollToTop extends HTMLElement {
+
+
+
+ connectedCallback() {
+ console.log("scroll to top");
+ this.addEventListener("click", () => {
+ window.scrollTo({top: 0, behavior: "smooth"});
+ });
+ let active = false;
+
+ if (this.innerHTML.trim() === "")
+ this.innerHTML = "⬆️";
+
+ window.addEventListener("scroll", () => {
+ if (window.scrollY > 300 && active === false) {
+ this.classList.add("show");
+ active = true;
+ }
+ if (window.scrollY < 300 && active === true) {
+ this.classList.remove("show");
+ active = false;
+ }
+ }, {passive: true});
+ }
+}
diff --git a/src/scrollspy/scrollspy.ts b/src/scrollspy/scrollspy.ts
new file mode 100755
index 0000000..a57adec
--- /dev/null
+++ b/src/scrollspy/scrollspy.ts
@@ -0,0 +1,52 @@
+import {ka_create_element, ka_debounce} from "@kasimirjs/embed";
+import {customElement} from "@kasimirjs/embed";
+import {ka_dom_ready} from "@kasimirjs/embed";
+
+
+@customElement("liscom-scrollspy")
+export class LiscomScrollspy extends HTMLElement {
+
+ private elements = {} as {[key: string]: {target: HTMLElement, nav: HTMLElement, observer: IntersectionObserver}};
+
+ public async connectedCallback() {
+ await ka_dom_ready();
+ // Allow attaching to any element
+ this.style.display = "contents";
+
+
+
+ document.querySelectorAll("[data-scrollspy-name]").forEach((el : HTMLElement) => {
+ let curName = el.getAttribute("data-scrollspy-name");
+ let navElem = ka_create_element("li", {class: ""},
+ [
+ ka_create_element("a", {href: window.location.pathname + "#" + el.getAttribute("id")}, curName)
+ ],
+ this
+ );
+
+
+ let curMo = new IntersectionObserver((entries) => {
+ if (entries[0].isIntersecting) {
+ navElem.classList.add("active");
+ } else {
+ navElem.classList.remove("active");
+ }
+ });
+ curMo.observe(el);
+ this.elements[curName] = ({target: el, nav: navElem, observer: curMo});
+ })
+
+ }
+
+ async disconnectedCallback() {
+ // disconnect all observers
+ for(let key in this.elements) {
+ this.elements[key].observer.disconnect();
+ this.elements[key].nav.remove();
+ }
+
+ }
+
+ // language=html
+
+}