Repository: xpodev/seamless (language: python)

Issue:
html, head and body attributes
All HTML tags, including `html`, `head` and `body` should inherit their properties from `HTMLElementProps`.


Discussion / hints:

Fix patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 7f7f91d..2e2cef7 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -40,7 +40,7 @@
       "name": "Python Debugger: Example",
       "type": "debugpy",
       "request": "launch",
-      "program": "${workspaceFolder}/examples/simple/main.py",
+      "program": "${workspaceFolder}/examples/simple/run.py",
       "env": {
         "PYTHONPATH": "${workspaceFolder}"
       },
@@ -51,7 +51,7 @@
       "name": "Python Debugger: SPA",
       "type": "debugpy",
       "request": "launch",
-      "program": "${workspaceFolder}/examples/spa/main.py",
+      "program": "${workspaceFolder}/examples/spa/run.py",
       "env": {
         "PYTHONPATH": "${workspaceFolder}"
       },
diff --git a/_auto_html.py b/_auto_html.py
index f3626fb..53b5afb 100644
--- a/_auto_html.py
+++ b/_auto_html.py
@@ -227,18 +227,15 @@
 
 html_map = {key: html_map.get(key, "HTMLElementProps") for key in html_classes}
 
-template = """from typing import TYPE_CHECKING, Unpack
-from ..element import Element
+template = """from typing_extensions import Unpack
+from pydom.element import Element
 
-if TYPE_CHECKING:
-    from ..types.html import {props_class_name}
-    from ..types import ChildType
+from ..types.html import {props_class_name}
+from ..types import ChildType
 
-class {class_name}(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["{props_class_name}"]):
-        super().__init__(*children, **kwargs)
 
-    tag_name = "{class_name_lower}"{inline}
+class {class_name}(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[{props_class_name}]): ...
 """
 
 inline_string = "\n    inline = True"
@@ -249,16 +246,14 @@ def __init__(self, *children: "ChildType", **kwargs: Unpack["{props_class_name}"
     class_name = cls
     class_name_lower = class_name.lower()
     props_class_name = props
-    filename = f"{class_name_lower}.py"
+    filename = f"{class_name_lower}.pyi"
     inline = inline_string if class_name in inline_html_classes else ""
 
     if class_name == "Meta":
         continue
 
     if class_name == "Del":
-        filename = "del_.py"
+        filename = "del_.pyi"
 
     with open(HTML_FILES / filename, "w") as f:
         f.write(template.format(class_name=class_name, class_name_lower=class_name_lower, props_class_name=props_class_name, inline=inline))
-
-    
diff --git a/docs/conf.py b/docs/conf.py
index e5146b3..eac12d9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,14 +1,4 @@
-import importlib.util
-import pathlib, importlib
-
-HERE = pathlib.Path(__file__).parent
-
-# Import the package to get the version
-spec = importlib.util.spec_from_file_location(
-    "seamless", str(HERE.parent / "seamless" / "version.py")
-)
-seamless = importlib.util.module_from_spec(spec)
-spec.loader.exec_module(seamless)
+from seamless.version import version as __version__
 
 # Configuration file for the Sphinx documentation builder.
 #
@@ -21,7 +11,7 @@
 project = "Seamless"
 copyright = "2024, Xpo Development"
 author = "Xpo Development"
-version = seamless.version
+version = __version__
 
 # -- General configuration ---------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
diff --git a/seamless/internal/__init__.py b/examples/simple/components/__init__.py
similarity index 100%
rename from seamless/internal/__init__.py
rename to examples/simple/components/__init__.py
diff --git a/examples/simple/components/app.py b/examples/simple/components/app.py
index 80f254b..2d4238c 100644
--- a/examples/simple/components/app.py
+++ b/examples/simple/components/app.py
@@ -1,14 +1,17 @@
 from seamless import Component, Div, Nav, Button
 from seamless.extensions import State
+from seamless.extra.transports.socketio.transport import SocketIOTransport
 from seamless.components.router import Router, Route, RouterLink
 from pages import HomePage, CounterPage, BasePage
-from components.loading import Loading
+
+from .loading import Loading
 
 
 class App(Component):
     def render(self):
         return BasePage(
             State.init(),
+            SocketIOTransport.init(),
             Div(class_name="d-flex flex-column h-100")(
                 Div(class_name="d-flex justify-content-between")(
                     Nav(class_name="navbar navbar-expand-lg navbar-light bg-light")(
diff --git a/examples/simple/main.py b/examples/simple/main.py
index 8aa1094..3b9b925 100644
--- a/examples/simple/main.py
+++ b/examples/simple/main.py
@@ -2,14 +2,14 @@
 from fastapi import FastAPI
 from fastapi.responses import HTMLResponse, FileResponse
 from seamless import render
-from seamless.middlewares import ASGIMiddleware
+from seamless.extra.transports.socketio.middleware import SocketIOMiddleware
 
 from components.app import App
 
 HERE = Path(__file__).parent
 
 app = FastAPI()
-app.add_middleware(ASGIMiddleware)
+app.add_middleware(SocketIOMiddleware)
 
 
 @app.get("/static/{file_path:path}")
@@ -25,4 +25,4 @@ def read_root():
 if __name__ == "__main__":
     import uvicorn
 
-    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
+    uvicorn.run(app, host="0.0.0.0", port=8000)
diff --git a/examples/simple/pages/base.py b/examples/simple/pages/base.py
index e2a858c..5971483 100644
--- a/examples/simple/pages/base.py
+++ b/examples/simple/pages/base.py
@@ -9,7 +9,7 @@ def head(self):
             rel="stylesheet",
             href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
         )
-        yield Script(src=f"https://cdn.jsdelivr.net/npm/@python-seamless/core@{__version__}/umd/seamless.init.js", defer=True)
+        yield Script(src=f"/static/seamless.init.js", defer=True)
         yield Style(
             "html, body { height: 100%; }" +
             CSS.to_css_string(minified=True)
diff --git a/examples/simple/pages/counter.py b/examples/simple/pages/counter.py
index 13eeeb0..de5130a 100644
--- a/examples/simple/pages/counter.py
+++ b/examples/simple/pages/counter.py
@@ -1,7 +1,7 @@
 from pydantic import BaseModel
 from seamless import Div, Form, Input, Button, Component
-from seamless.core import SocketID
 from seamless.extensions import State
+from seamless.extra.transports.client_id import ClientID
 from seamless.types.events import SubmitEvent
 
 
@@ -47,7 +47,7 @@ def render(self):
             ),
         )
 
-    def submit(self, event: SubmitEvent[Counter], sid: SocketID):
+    def submit(self, event: SubmitEvent[Counter], sid: ClientID):
         print("Form submitted!")
         print("socket id:", sid)
         print(event)
diff --git a/examples/simple/run.py b/examples/simple/run.py
new file mode 100644
index 0000000..cff4a72
--- /dev/null
+++ b/examples/simple/run.py
@@ -0,0 +1,4 @@
+if __name__ == "__main__":
+    import uvicorn
+
+    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
diff --git a/examples/spa/components/app.py b/examples/spa/components/app.py
index f8bf881..29cf9bc 100644
--- a/examples/spa/components/app.py
+++ b/examples/spa/components/app.py
@@ -1,7 +1,7 @@
 from seamless import Component, Div, Nav, Button
 from seamless.context.context import Context
 from seamless.styling import StyleObject
-from seamless.extensions import State
+from seamless.extensions import State, SocketIOTransport
 from seamless.components.router import Router, Route, RouterLink
 from pages import HomePage, CounterPage, BasePage, UserPage
 from components.loading import Loading
@@ -12,6 +12,7 @@ class App(Component):
     def render(self):
         return BasePage(
             State.init(),
+            SocketIOTransport.init(),
             Div(class_name="d-flex flex-column h-100")(
                 Div(class_name="d-flex justify-content-between")(
                     Nav(class_name="navbar navbar-expand-lg navbar-light bg-light")(
@@ -19,7 +20,7 @@ def render(self):
                         RouterLink(to="/counter", class_name="navbar-brand")("Counter"),
                     ),
                     Div(
-                        Button(on_click=self.foo, style=StyleObject(border_radius="5px", background_color="red"))(
+                        Button(on_click=foo, style=StyleObject(border_radius="5px", background_color="red"))(
                             "Click me!"
                         )
                     )
@@ -36,5 +37,5 @@ def render(self):
             title="Seamless",
         )
 
-    def foo(self, event, context: Context):
-        print("foo")
\ No newline at end of file
+def foo(event, context: Context):
+    print("foo")
\ No newline at end of file
diff --git a/examples/spa/components/usage.py b/examples/spa/components/usage.py
index 184bf6f..a304fb0 100644
--- a/examples/spa/components/usage.py
+++ b/examples/spa/components/usage.py
@@ -1,11 +1,38 @@
-from seamless import Component, Div
+from seamless import Component, Div, Table, Th, Tr, Td, H2
 from seamless.context import Context
 from seamless.extra.events import EventsFeature
 
+
 class Usage(Component, inject_render=True):
     def render(self, context: Context):
         events = context.get_feature(EventsFeature)
+        total_scoped = sum(len(scope) for scope in events.DB.scoped_events.values())
 
         return Div(
-            f"Events: {len(events.DB.events)}"
-        )
\ No newline at end of file
+            H2(f"Global Events - Total: {len(events.DB.events)}"),
+            Table(
+                Tr(
+                    Th("Event ID"),
+                ),
+                *(
+                    Tr(
+                        Td(event_id),
+                    )
+                    for event_id in events.DB.events
+                ),
+            ),
+            H2(f"Actions - Total: {total_scoped}"),
+            Table(
+                Tr(
+                    Th("Scope ID"),
+                    Th("Actions IDs"),
+                ),
+                *(
+                    Tr(
+                        Td(scope),
+                        Td(", ".join(actions.keys()))
+                    )
+                    for scope, actions in events.DB.scoped_events.items()
+                )
+            ),
+        )
diff --git a/examples/spa/main.py b/examples/spa/main.py
index 8aa1094..3a61dbe 100644
--- a/examples/spa/main.py
+++ b/examples/spa/main.py
@@ -1,15 +1,16 @@
 from pathlib import Path
+
 from fastapi import FastAPI
 from fastapi.responses import HTMLResponse, FileResponse
 from seamless import render
-from seamless.middlewares import ASGIMiddleware
+from seamless.extra.transports.socketio.middleware import SocketIOMiddleware
 
 from components.app import App
 
 HERE = Path(__file__).parent
 
 app = FastAPI()
-app.add_middleware(ASGIMiddleware)
+app.add_middleware(SocketIOMiddleware)
 
 
 @app.get("/static/{file_path:path}")
@@ -25,4 +26,4 @@ def read_root():
 if __name__ == "__main__":
     import uvicorn
 
-    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
+    uvicorn.run(app, host="0.0.0.0", port=8000)
diff --git a/examples/spa/pages/base.py b/examples/spa/pages/base.py
index cf8bd41..5971483 100644
--- a/examples/spa/pages/base.py
+++ b/examples/spa/pages/base.py
@@ -9,7 +9,7 @@ def head(self):
             rel="stylesheet",
             href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
         )
-        yield Script(src=f"https://cdn.jsdelivr.net/npm/@python-seamless/core@0.9.0/umd/seamless.init.js", defer=True)
+        yield Script(src=f"/static/seamless.init.js", defer=True)
         yield Style(
             "html, body { height: 100%; }" +
             CSS.to_css_string(minified=True)
diff --git a/examples/spa/pages/counter.py b/examples/spa/pages/counter.py
index c29e4fc..73291ba 100644
--- a/examples/spa/pages/counter.py
+++ b/examples/spa/pages/counter.py
@@ -1,6 +1,6 @@
 from pydantic import BaseModel
 from seamless import Div, Form, Input, Button, Component
-from seamless.core import SocketID
+from seamless.extra.transports.client_id import ClientID
 from seamless.extensions import State
 from seamless.types.events import SubmitEvent
 
@@ -50,13 +50,13 @@ def render(self):
             ),
         )
 
-    def submit(self, event: SubmitEvent[Counter], sid: SocketID):
+    def submit(self, event: SubmitEvent[Counter], sid: ClientID):
         print("Form submitted!")
         print("socket id:", sid)
         print(event)
 
 
-def global_submit(event, sid: SocketID):
+def global_submit(event, sid: ClientID):
     print("Global submit!")
     print("socket id:", sid)
     print(event)
diff --git a/examples/spa/run.py b/examples/spa/run.py
new file mode 100644
index 0000000..cff4a72
--- /dev/null
+++ b/examples/spa/run.py
@@ -0,0 +1,4 @@
+if __name__ == "__main__":
+    import uvicorn
+
+    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
diff --git a/node/package.json b/node/package.json
index 81e5ba6..dc8305e 100644
--- a/node/package.json
+++ b/node/package.json
@@ -18,9 +18,9 @@
   "scripts": {
     "build": "tsc -b ./packages",
     "build:all": "tsc -b ./packages && npm run build:browser && npm run build:react",
-    "build:browser": "webpack --config ./packages/core/webpack.config.js --entry ./packages/core/src/index.ts --output-filename seamless.min.js",
-    "build:browser:init": "webpack --config ./packages/core/webpack.config.js --entry ./packages/core/src/init.ts --output-filename seamless.init.js",
+    "build:browser": "webpack --config ./packages/core/webpack.config.js",
     "build:react": "webpack --config ./packages/react/webpack.config.js",
-    "watch": "tsc -b ./packages -w"
+    "watch": "tsc -b ./packages -w",
+    "webpack": "webpack"
   }
 }
\ No newline at end of file
diff --git a/node/packages/core/package.json b/node/packages/core/package.json
index b3e9924..8937db9 100644
--- a/node/packages/core/package.json
+++ b/node/packages/core/package.json
@@ -1,5 +1,5 @@
 {
-  "name": "@python-seamless/core",
+  "name": "python-seamless",
   "version": "0.1.2",
   "description": "JavaScript integration for Seamless",
   "main": "dist/index.js",
diff --git a/node/packages/core/src/constants.ts b/node/packages/core/src/constants.ts
new file mode 100644
index 0000000..fe0c1d9
--- /dev/null
+++ b/node/packages/core/src/constants.ts
@@ -0,0 +1,4 @@
+export const SEAMLESS_ELEMENT = "seamless:element";
+export const SEAMLESS_EMPTY = "seamless:empty";
+export const SEAMLESS_INIT = "seamless:init";
+export const SEAMLESS_INIT_ASYNC = "seamless:async";
\ No newline at end of file
diff --git a/node/packages/core/src/index.ts b/node/packages/core/src/index.ts
index ee0c422..99dc73b 100644
--- a/node/packages/core/src/index.ts
+++ b/node/packages/core/src/index.ts
@@ -1,21 +1,21 @@
-import io from "socket.io-client";
-import { SeamlessOptions, OutEvent } from "./types";
+import type {
+  SeamlessOptions,
+  OutEvent,
+  SeamlessElement,
+  Primitive,
+} from "./types";
 export { SeamlessOptions };
 
-export type Primitive = string | number | boolean | null;
+import {
+  SEAMLESS_ELEMENT,
+  SEAMLESS_INIT,
+  SEAMLESS_EMPTY,
+  SEAMLESS_INIT_ASYNC,
+} from "./constants";
 
-export interface SeamlessElement {
-  type: string;
-  props: Record<string, any>;
-  children: Array<SeamlessElement | Primitive> | null;
-}
-
-const SEAMLESS_ELEMENT = "seamless:element";
-const SEAMLESS_INIT = "seamless:init";
-const SEAMLESS_EMPTY = "seamless:empty";
+const AsyncFunction = new Function("return (async function () {}).constructor")();
 
 class Seamless {
-  protected readonly socket;
   private readonly eventObjectTransformer: (
     originalEvent: Event,
     outEvent: any
@@ -23,15 +23,10 @@ class Seamless {
   private readonly context: Record<any, any> = {};
 
   constructor(config?: SeamlessOptions) {
-    this.socket = io({
-      reconnectionDelayMax: 10000,
-      ...config?.socketOptions,
-    });
     this.eventObjectTransformer =
       config?.eventObjectTransformer || ((_, outEvent) => outEvent);
 
     this.context.instance = this;
-    this.socket.on("error", (error) => this.handleErrors(error));
     this.init();
   }
 
@@ -99,16 +94,20 @@ class Seamless {
   protected attachInit(element: HTMLElement) {
     const initCode = element.getAttribute(SEAMLESS_INIT);
     if (initCode) {
-      new Function("seamless", initCode).apply(element, [this.context]);
+      new (element.hasAttribute(SEAMLESS_INIT_ASYNC)
+        ? AsyncFunction as { new (): Function }
+        : Function)("seamless", initCode).apply(element, [this.context]);
+
+      element.removeAttribute(SEAMLESS_INIT_ASYNC);
       element.removeAttribute(SEAMLESS_INIT);
     }
   }
 
   protected initEmpty(element: HTMLElement) {
     while (element.firstChild) {
-      element.parentElement?.insertBefore(element.firstChild, element);
+      element.parentNode?.insertBefore(element.firstChild, element);
     }
-    element.parentElement?.removeChild(element);
+    element.parentNode?.removeChild(element);
   }
 
   protected isPrimitive(value: any): value is Primitive {
@@ -121,35 +120,6 @@ class Seamless {
     );
   }
 
-  async getComponent(name: string, props: Record<string, any>) {
-    return await this.sendWaitResponse<SeamlessElement | Primitive>(
-      "component",
-      name,
-      props
-    );
-  }
-
-  registerEventListener(
-    event: string,
-    callback: (e: any) => any
-  ) {
-    this.socket.on(event, callback);
-  }
-
-  emit(event: string, ...args: any[]) {
-    this.socket.emit(event, ...args);
-  }
-
-  sendWaitResponse<T>(event: string, ...args: any[]) {
-    return new Promise<T>((resolve) => {
-      this.socket.emit(event, ...args, resolve);
-    });
-  }
-
-  protected handleErrors(error: any) {
-    throw new Error(error);
-  }
-
   protected serializeEventObject(event: Event) {
     const outEvent: OutEvent = {
       type: event.type,
diff --git a/node/packages/core/src/types.ts b/node/packages/core/src/types.ts
index b8f033b..ee020d6 100644
--- a/node/packages/core/src/types.ts
+++ b/node/packages/core/src/types.ts
@@ -18,3 +18,10 @@ export interface OutEvent<T = any> {
   data: T;
 }
 
+export type Primitive = string | number | boolean | null;
+
+export interface SeamlessElement {
+  type: string;
+  props: Record<string, any>;
+  children: Array<SeamlessElement | Primitive> | null;
+}
\ No newline at end of file
diff --git a/node/packages/core/webpack.config.js b/node/packages/core/webpack.config.js
index e24ab5c..4fe31d7 100644
--- a/node/packages/core/webpack.config.js
+++ b/node/packages/core/webpack.config.js
@@ -4,6 +4,10 @@ const TerserPlugin = require('terser-webpack-plugin');
 const here = path.resolve(__dirname);
 
 module.exports = {
+  entry: {
+    min: path.resolve(here, 'src/index.ts'),
+    init: path.resolve(here, 'src/init.ts'),
+  },
   module: {
     rules: [
       {
@@ -18,6 +22,7 @@ module.exports = {
   },
   output: {
     path: path.resolve(here, 'umd'),
+    filename: 'seamless.[name].js',
     library: 'Seamless',
     libraryTarget: 'umd',
     libraryExport: 'default',
diff --git a/pyproject.toml b/pyproject.toml
index 1fbf687..3db2ea9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,15 +1,13 @@
 [project]
 name = "python-seamless"
-authors = [
-  { name="Xpo Development", email="dev@xpo.dev" },
-]
+authors = [{ name = "Xpo Development", email = "dev@xpo.dev" }]
 description = "A Python package for creating and manipulating HTML components. It is working similar to React.js, but in Python"
 readme = "README.md"
-requires-python = ">=3.10"
+requires-python = ">=3.11"
 classifiers = [
-    "Programming Language :: Python :: 3",
-    "License :: OSI Approved :: MIT License",
-    "Operating System :: OS Independent",
+  "Programming Language :: Python :: 3",
+  "License :: OSI Approved :: MIT License",
+  "Operating System :: OS Independent",
 ]
 dynamic = ["dependencies", "version"]
 
@@ -30,4 +28,5 @@ exclude = ["tests*"]
 seamless = ["**/*.js"]
 
 [tool.setuptools.dynamic]
-version = {attr="seamless.__version__"}
+version = { attr = "seamless.version.version" }
+dependencies = { file = ["requirements.txt"] }
diff --git a/requirements.txt b/requirements.txt
index 8efb4c7..46ef01d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
-python-socketio==5.11.1
 cssutils==2.9.0
+python-dom
+python-socketio==5.11.1
 typing-extensions
\ No newline at end of file
diff --git a/seamless/__init__.py b/seamless/__init__.py
index ffe50c2..d8a6cba 100644
--- a/seamless/__init__.py
+++ b/seamless/__init__.py
@@ -1,9 +1,22 @@
 import os
 
+from pydom import Component
+
+from .context import Context, set_global_context
+from .core import JS
+from .internal.constants import DISABLE_GLOBAL_CONTEXT_ENV
+from .html import *
+from .rendering import render
 from .version import version as __version__
 
-if not os.environ.get("SEAMLESS_VERSION_ONLY", False):
-    from ._init import *
+
+if not os.getenv(DISABLE_GLOBAL_CONTEXT_ENV):
+    set_global_context(Context.standard())
+else:
+    set_global_context(None)  # type: ignore
+
+del set_global_context
+del DISABLE_GLOBAL_CONTEXT_ENV
 
 __all__ = [
     "Component",
diff --git a/seamless/_init.py b/seamless/_init.py
deleted file mode 100644
index 4295ac3..0000000
--- a/seamless/_init.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from .context import Context
-from .core import JS
-from .rendering import render
-from .core import Component
-from .html import *
-from .version import version as __version__
-
-__all__ = [
-    "Component",
-    "A",
-    "Abbr",
-    "Address",
-    "Area",
-    "Article",
-    "Aside",
-    "B",
-    "Base",
-    "BlockQuote",
-    "Body",
-    "Br",
-    "Button",
-    "Canvas",
-    "Cite",
-    "Code",
-    "Col",
-    "Div",
-    "Em",
-    "Embed",
-    "Footer",
-    "Form",
-    "Fragment",
-    "H1",
-    "H2",
-    "H3",
-    "H4",
-    "H5",
-    "H6",
-    "Head",
-    "Header",
-    "Hr",
-    "Html",
-    "I",
-    "Img",
-    "Input",
-    "Label",
-    "Li",
-    "Link",
-    "Main",
-    "Meta",
-    "Nav",
-    "Ol",
-    "Option",
-    "P",
-    "Param",
-    "Pre",
-    "Script",
-    "Section",
-    "Select",
-    "Small",
-    "Source",
-    "Span",
-    "Strong",
-    "Style",
-    "Sub",
-    "Sup",
-    "Table",
-    "TBody",
-    "Td",
-    "TextArea",
-    "Th",
-    "THead",
-    "Time",
-    "Title",
-    "Tr",
-    "Track",
-    "U",
-    "Ul",
-    "Wbr",
-    "Context",
-    "JS",
-    "render",
-]
diff --git a/seamless/components/page.py b/seamless/components/page.py
index b38b353..c59c62b 100644
--- a/seamless/components/page.py
+++ b/seamless/components/page.py
@@ -1,7 +1,8 @@
-from typing import Literal, overload, Iterable
-from typing_extensions import TypedDict
+from typing import overload, Iterable
+
+from pydom import Component
+from pydom.utils.functions import to_iter
 
-from ..internal.utils import to_iter
 from ..html import (
     Fragment,
     Html,
@@ -10,48 +11,53 @@
     Body,
     Meta,
 )
-from ..types import ChildrenType, ChildType
-
-from ..core.component import Component
-
-
-class _HtmlProps(TypedDict, total=False, closed=False):
-    lang: str
-
-
-class _HeadProps(TypedDict, total=False, closed=False): ...
-
 
-class _BodyProps(TypedDict, total=False, closed=False):
-    dir: Literal["ltr", "rtl"]
+from ..types import ChildType, ChildrenType
+from ..types.html import HTMLHtmlElement, HTMLBodyElement, HTMLHeadElement
 
 
-class Page(Component, name="SeamlessBasePage"):
+class Page(Component):
     @overload
-    def __init__(self, *children: ChildType, title: str | None = None, html_props: _HtmlProps | None = None, head_props: _HeadProps | None = None, body_props: _BodyProps | None = None): ...
+    def __init__(
+        self,
+        *children: ChildType,
+        title: str | None = None,
+        html_props: HTMLHtmlElement | None = None,
+        head_props: HTMLHeadElement | None = None,
+        body_props: HTMLBodyElement | None = None,
+    ): ...
     @overload
-    def __init__(self, *, children: ChildrenType, title: str | None = None, html_props: _HtmlProps | None = None, head_props: _HeadProps | None = None, body_props: _BodyProps | None = None): ...
-    def __init__( # type: ignore
+    def __init__(
         self,
         *,
+        children: ChildrenType,
         title: str | None = None,
-        html_props: _HtmlProps | None = None,
-        head_props: _HeadProps | None = None,
-        body_props: _BodyProps | None = None,
+        html_props: HTMLHtmlElement | None = None,
+        head_props: HTMLHeadElement | None = None,
+        body_props: HTMLBodyElement | None = None,
+    ): ...
+
+    def __init__(  # type: ignore
+        self,
+        *,
+        title: str | None = None,
+        html_props: HTMLHtmlElement | None = None,
+        head_props: HTMLHeadElement | None = None,
+        body_props: HTMLBodyElement | None = None,
     ):
         self.title = title
         self._html_props = html_props or {"lang": "en"}
         self._head_props = head_props or {}
         self._body_props = body_props or {"dir": "ltr"}
 
-    def head(self) -> Iterable[ChildType]:
+    def head(self) -> Iterable["ChildType"]:
         """
         The children that will be inside the `head` tag.
         """
         return (
             Meta(charset="UTF-8"),
             Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
-            Title(self.title),
+            Title(self.title) if self.title else None,
         )
 
     def body(self) -> Iterable[ChildType]:
@@ -82,3 +88,5 @@ def __init__(self, *args, **kwargs):
             original_init(self, *args, **kwargs)
 
         cls.__init__ = __init__
+
+    __seamless_name__ = "SeamlessBasePage"
diff --git a/seamless/components/router/route.py b/seamless/components/router/route.py
index 1a1ccba..9279c10 100644
--- a/seamless/components/router/route.py
+++ b/seamless/components/router/route.py
@@ -1,4 +1,4 @@
-from ...core import Component
+from pydom import Component
 
 
 class Route(Component):
diff --git a/seamless/components/router/router.js b/seamless/components/router/router.js
index 9755927..618458c 100644
--- a/seamless/components/router/router.js
+++ b/seamless/components/router/router.js
@@ -18,9 +18,18 @@ class FloatConvertor {
   }
 }
 
+class PathConvertor {
+  static regex = "[^?]+";
+
+  static convert(value) {
+    return value;
+  }
+}
+
 const convertors = {
   int: IntConvertor,
   float: FloatConvertor,
+  path: PathConvertor,
 };
 
 routes = routes.map((route) => {
@@ -67,12 +76,6 @@ routes = routes.map((route) => {
   };
 });
 
-if (loadingComponentName) {
-  seamless.instance.getComponent(loadingComponentName, {}).then((component) => {
-    loadingComponent = seamless.instance.toDOMElement(component);
-  });
-}
-
 const clearParent = () => {
   while (parent.firstChild) {
     parent.removeChild(parent.firstChild);
@@ -81,7 +84,7 @@ const clearParent = () => {
 
 const loadComponent = async (name, props = {}) => {
   return seamless.instance.toDOMElement(
-    await seamless.instance.getComponent(name, props)
+    await seamless.getComponent(name, props)
   );
 };
 
@@ -93,14 +96,16 @@ window.addEventListener("pageLocationChange", () => {
     const match = path.match(route.regex);
     if (match) {
       page = route;
-      page.params = match.groups ? Object.fromEntries(
-        Object.entries(match.groups).map(([key, value]) => [
-          key,
-          route.regexConvertors[key]
-            ? route.regexConvertors[key].convert(value)
-            : value,
-        ])
-      ) : {};
+      page.params = match.groups
+        ? Object.fromEntries(
+            Object.entries(match.groups).map(([key, value]) => [
+              key,
+              route.regexConvertors[key]
+                ? route.regexConvertors[key].convert(value)
+                : value,
+            ])
+          )
+        : {};
       break;
     }
   }
@@ -109,7 +114,9 @@ window.addEventListener("pageLocationChange", () => {
     return;
   }
 
-  const props = Object.fromEntries(new URLSearchParams(window.location.search).entries());
+  const props = Object.fromEntries(
+    new URLSearchParams(window.location.search).entries()
+  );
 
   clearParent();
   if (loadingComponent) {
@@ -128,4 +135,12 @@ seamless.navigateTo = function (to) {
   return false;
 };
 
-window.dispatchEvent(PageStateChange);
+window.addEventListener("transportsAvailable", async (event) => {
+  if (loadingComponentName) {
+    seamless.getComponent(loadingComponentName, {}).then((component) => {
+      loadingComponent = seamless.instance.toDOMElement(component);
+    });
+  }
+
+  window.dispatchEvent(PageStateChange);
+});
diff --git a/seamless/components/router/router.py b/seamless/components/router/router.py
index 8f4675c..f5b5dea 100644
--- a/seamless/components/router/router.py
+++ b/seamless/components/router/router.py
@@ -1,9 +1,11 @@
 from json import dumps
 from pathlib import Path
-from typing import overload
+from typing import Optional, Tuple, overload, Type
 
-from ...core import Empty, Component, JS
+from pydom import Component
 
+from ...core import Empty, JS
+from ...extra.components import component_name
 from .route import Route
 
 
@@ -11,26 +13,33 @@
 
 
 class Router(Component):
-    children: tuple[Route, ...] # type: ignore
+    children: Tuple[Route, ...]  # type: ignore
 
     @overload
-    def __init__(self, *, loading_component: type[Component] | None = None): ...
+    def __init__(self, *, loading_component: Optional[Type[Component]] = None): ...
     @overload
-    def __init__(self, *routes: Route, loading_component: type[Component] | None = None): ...
-    
-    def __init__(self, *, loading_component: type[Component] | None = None): # type: ignore
-        self.loading_component = loading_component.__seamless_name__ if loading_component else None
+    def __init__(
+        self, *routes: Route, loading_component: Optional[Type[Component]] = None
+    ): ...
+
+    def __init__(self, *, loading_component: Optional[Type[Component]] = None):  # type: ignore
+        self.loading_component = (
+            component_name(loading_component) if loading_component else None
+        )
         for route in self.children:
             if not isinstance(route, Route):
                 raise TypeError(f"Expected Route, got {type(route)}")
 
     def render(self):
         routes = [
-            {"path": route.path, "name": route.component.__seamless_name__}
+            {"path": route.path, "name": component_name(route.component)}
             for route in self.children
         ]
 
         with open(HERE / "router.js", "r") as f:
             router_js = f.read()
 
-        return Empty(init=JS(f"let routes = {dumps(routes)};{router_js}"), loading=self.loading_component)
+        return Empty(
+            init=JS(f"let routes = {dumps(routes)};{router_js}"),
+            loading=self.loading_component,
+        )
diff --git a/seamless/context/__init__.py b/seamless/context/__init__.py
index c599496..5942203 100644
--- a/seamless/context/__init__.py
+++ b/seamless/context/__init__.py
@@ -1,4 +1,4 @@
 
-from .context import Context, get_context
+from .context import Context, get_context, set_global_context
 
-__all__ = ["Context", "get_context"]
\ No newline at end of file
+__all__ = ["Context", "get_context", "set_global_context"]
\ No newline at end of file
diff --git a/seamless/context/context.py b/seamless/context/context.py
index 68fde5f..33170ee 100644
--- a/seamless/context/context.py
+++ b/seamless/context/context.py
@@ -1,80 +1,39 @@
-from typing import Callable, Concatenate, ParamSpec, Any, TYPE_CHECKING, TypeVar
-from socketio import AsyncServer
-
-from ..errors import ClientError
-
+import os
+from typing import (
+    Any,
+    Callable,
+    Concatenate,
+    Optional,
+    ParamSpec,
+    TypeVar,
+    cast,
+)
+
+from pydom.context.context import (
+    Context as _Context,
+    get_context as _get_context,
+    set_default_context as _set_global_context,
+)
+from pydom.rendering.tree.nodes import ContextNode
+
+from ..errors import Error
+from ..internal.constants import DISABLE_GLOBAL_CONTEXT_ENV
 from ..internal.injector import Injector
-from ..internal.utils import to_async, wraps
-
-from .request import WSRequest, set_request
-
-if TYPE_CHECKING:
-    from ..rendering.tree import ElementNode
-
-T = TypeVar("T")
 
+T = TypeVar("T", bound=_Context)
 P = ParamSpec("P")
+
 Feature = Callable[Concatenate["Context", P], Any]
+PropertyMatcher = Callable[Concatenate[str, Any, P], bool] | str
+PropertyTransformer = Callable[Concatenate[str, Any, "ContextNode", P], None]
+PostRenderTransformer = Callable[Concatenate["ContextNode", P], None]
 
-PropertyMatcher = Callable[Concatenate[str, Any, ...], bool] | str
-PropertyTransformer = Callable[Concatenate[str, Any, "ElementNode", ...], None]
-PostRenderTransformer = Callable[Concatenate["ElementNode", ...], None]
 
-class Context:
+class Context(_Context):
     def __init__(self) -> None:
-        self.server = AsyncServer(async_mode="asgi")
+        super().__init__()
         self.injector = Injector()
         self.injector.add(Context, self)
-        self._prop_transformers: list[tuple[PropertyMatcher, PropertyTransformer]] = []
-        self._post_render_transformers: list[PostRenderTransformer] = []
-        self._features: dict[type, Any] = {}
-
-    def on(self, event, handler):
-        @wraps(handler)
-        async def wrapper(sid, *args, **kwargs):
-            try:
-                WSRequest.make(sid)
-                result = await to_async(handler)(sid, *args, **kwargs)
-                set_request(None)
-                return result
-            except ClientError as e:
-                await self.server.emit("error", str(e), to=sid)
-            except Exception as e:
-                await self.server.emit("error", str(e), to=sid)
-                raise e
-
-        self.server.on(event, wrapper)
-
-    async def emit(self, event: str, *args):
-        return await self.server.emit(event, *args)
-
-    def add_feature(self, feature: Feature[P], *args: P.args, **kwargs: P.kwargs):
-            result = feature(self, *args, **kwargs)
-            if isinstance(feature, type):
-                self._features[feature] = result
-
-    def get_feature(self, feature: type[T]) -> T:
-        return self._features[feature]
-
-    def add_prop_transformer(
-        self,
-        matcher: PropertyMatcher,
-        transformer: PropertyTransformer
-    ):
-        self._prop_transformers.append((matcher, self.inject(transformer)))
-
-    def add_post_render_transformer(
-        self, transformer: PostRenderTransformer
-    ):
-        self._post_render_transformers.append(self.inject(transformer))
-
-    @property
-    def prop_transformers(self):
-        return self._prop_transformers
-    
-    @property
-    def post_render_transformers(self):
-        return self._post_render_transformers
 
     @classmethod
     def standard(cls) -> "Context":
@@ -85,17 +44,24 @@ def standard(cls) -> "Context":
         add_standard_features(context)
         return context
 
-    def inject(self, callback: Callable) -> Callable:
-        return self.injector.inject(callback)
 
+def get_context(context: Optional[Context] = None):
+    if context is None:
+        context = cast(Optional[Context], _get_context())
 
-_GLOBAL_CONTEXT = Context.standard()
+    if context is None:
+        if os.getenv(DISABLE_GLOBAL_CONTEXT_ENV):
+            raise Error(
+                f"Global context is disabled by {DISABLE_GLOBAL_CONTEXT_ENV} environment variable. "
+                "You must provide a context explicitly. Did you forget to call set_global_context?"
+            ) from None
 
+        raise Error(
+            "No global context found. Did you forget to call set_global_context?"
+        )
 
-def get_context(context: Context | None = None) -> Context:
-    return context or _GLOBAL_CONTEXT
+    return context
 
 
-def set_global_context(context: Context):
-    global _GLOBAL_CONTEXT
-    _GLOBAL_CONTEXT = context
\ No newline at end of file
+def set_global_context(context: _Context):
+    _set_global_context(context)
diff --git a/seamless/context/default.py b/seamless/context/default.py
index 1774c62..84f5b06 100644
--- a/seamless/context/default.py
+++ b/seamless/context/default.py
@@ -1,30 +1,21 @@
-from typing import TYPE_CHECKING
+from pydom.context.standard import add_standard_features as pydom_standard_features
 
+
+from .context import Context
 from ..extra.components import ComponentsFeature
 from ..extra.events import EventsFeature
 from ..extra.state import StateFeature
-
-from ..extra.transformers.class_transformer import class_transformer
-from ..extra.transformers.dash_transformer import dash_transformer
 from ..extra.transformers.js_transformer import init_transformer, js_transformer
-from ..extra.transformers.html_events_transformer import html_events_transformer
-from ..extra.transformers.simple_transformer import simple_transformer
-from ..extra.transformers.style_transformer import style_transformer
+from ..extra.transports.socketio.transport import SocketIOTransport
 
-if TYPE_CHECKING:
-    from . import Context
 
+def add_standard_features(ctx: Context):
+    pydom_standard_features(ctx)
 
-def add_standard_features(ctx: "Context"):
+    ctx.add_feature(SocketIOTransport)
     ctx.add_feature(ComponentsFeature)
-    ctx.add_feature(EventsFeature, claim_time=30)
+    ctx.add_feature(EventsFeature)
     ctx.add_feature(StateFeature)
 
-    # Order matters
-    ctx.add_prop_transformer(*class_transformer())
-    ctx.add_prop_transformer(*simple_transformer())
-    ctx.add_prop_transformer(*html_events_transformer())
-    ctx.add_prop_transformer(*dash_transformer())
     ctx.add_prop_transformer(*init_transformer())
     ctx.add_prop_transformer(*js_transformer())
-    ctx.add_prop_transformer(*style_transformer())
diff --git a/seamless/context/request.py b/seamless/context/request.py
deleted file mode 100644
index a6cd233..0000000
--- a/seamless/context/request.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from enum import Enum
-from dataclasses import dataclass
-from urllib.parse import parse_qs
-
-from ..internal.cookies import Cookies
-
-
-class RequestType(Enum):
-    HTTP = "http"
-    WS = "ws"
-
-
-class Request:
-    type: RequestType
-
-    @classmethod
-    def make(cls, request_data, **kwargs):
-        request = cls(request_data, **kwargs)
-        set_request(request)
-        return request
-
-
-@dataclass
-class WSRequest(Request):
-    type = RequestType.WS
-    socket_id: str
-
-
-class HTTPRequest(Request):
-    type = RequestType.HTTP
-    path: str
-    method: str
-    query: dict[str, str | list[str]]
-    headers: dict
-    cookies: Cookies
-
-    id: str = None
-
-    def __init__(self, raw_request: dict, *, type=None):
-        self._raw_request = raw_request
-        self._parse()
-
-    def _parse_headers(self, headers: dict):
-        return {k.decode(): v.decode() for k, v in headers}
-
-    def _parse_query(self, query_string: str):
-        self._raw_query = query_string
-        return {
-            k: v[0] if len(v) == 1 else [i for i in v]
-            for k, v in parse_qs(query_string).items()
-        }
-
-    def _parse(self):
-        self.method = self._raw_request["method"]
-        self.path = self._raw_request["path"]
-        self.query = self._parse_query(self._raw_request["query_string"])
-        self.headers = self._parse_headers(self._raw_request["headers"])
-        self.cookies = Cookies.from_request_headers(self.headers)
-
-    @property
-    def full_path(self):
-        return f"{self.path}?{self._raw_query}"
-
-
-_request: WSRequest | HTTPRequest | None = None
-
-
-def request():
-    return _request
-
-
-def set_request(request: WSRequest | HTTPRequest | None):
-    global _request
-    _request = request
diff --git a/seamless/core/__init__.py b/seamless/core/__init__.py
index 1bea950..a29f6c1 100644
--- a/seamless/core/__init__.py
+++ b/seamless/core/__init__.py
@@ -1,12 +1,8 @@
-from .component import Component
 from .empty import Empty
 from .javascript import JavaScript, JS
-from .sid import SocketID
 
 __all__ = [
-    "Component",
     "Empty",
     "JavaScript",
     "JS",
-    "SocketID",
 ]
diff --git a/seamless/core/component.py b/seamless/core/component.py
deleted file mode 100644
index d25789e..0000000
--- a/seamless/core/component.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from abc import abstractmethod, ABC
-from typing import TYPE_CHECKING, ClassVar, overload
-
-if TYPE_CHECKING:
-    from ..types import RenderResult, ChildType
-
-
-class Component(ABC):
-    children: tuple["ChildType", ...]
-    __seamless_name__: ClassVar[str] = "Component"
-
-    def __init__(self, *children: "ChildType") -> None:
-        self.children = children
-
-    @abstractmethod
-    def render(self, *_, **kwargs) -> "RenderResult": ...
-
-    def __init_subclass__(cls, **kwargs) -> None:
-        super().__init_subclass__(**kwargs)
-
-        if cls.__init__ is Component.__init__:
-            return
-
-        original_init = cls.__init__
-
-        def __init__(self, *args, children=None, **kwargs):
-            Component.__init__(self, *(getattr(self, "children", children) or args))
-            original_init(self, **kwargs)
-
-        cls.__init__ = __init__
-
-    def __call__(self, *children: "ChildType"):
-        self.children = children
-        return self
diff --git a/seamless/core/empty.py b/seamless/core/empty.py
index 58ae6cf..fa55fe0 100644
--- a/seamless/core/empty.py
+++ b/seamless/core/empty.py
@@ -1,5 +1,6 @@
 from typing import TYPE_CHECKING, Any
-from ..element import Element
+from pydom.element import Element
+
 from ..internal.constants import SEAMLESS_ELEMENT_ATTRIBUTE
 
 if TYPE_CHECKING:
diff --git a/seamless/core/javascript.py b/seamless/core/javascript.py
index d7230b9..ee80bbd 100644
--- a/seamless/core/javascript.py
+++ b/seamless/core/javascript.py
@@ -4,11 +4,11 @@
 
 class JavaScript:
     @overload
-    def __init__(self, code: str) -> None: ...
+    def __init__(self, code: str, *, async_: bool = False) -> None: ...
     @overload
-    def __init__(self, *, file: str | PathLike) -> None: ...
+    def __init__(self, *, file: str | PathLike, async_: bool = False) -> None: ...
 
-    def __init__(self, code=None, *, file=None) -> None:
+    def __init__(self, code=None, *, file=None, async_: bool = False) -> None:
         if file:
             if code:
                 raise ValueError("Cannot specify both code and file")
@@ -17,12 +17,13 @@ def __init__(self, code=None, *, file=None) -> None:
         elif not code:
             raise ValueError("Must specify either code or file")
         self.code = code
+        self.async_ = async_
 
     def __add__(self, other: "JavaScript | str") -> "JavaScript":
         if isinstance(other, JavaScript):
-            return JavaScript(self.code + other.code)
+            return JavaScript(self.code + other.code, async_=self.async_ or other.async_)
         elif isinstance(other, str):
-            return JavaScript(self.code + other)
+            return JavaScript(self.code + other, async_=self.async_)
         else:
             raise TypeError(
                 f"Cannot concatenate JavaScript with {type(other).__name__}"
diff --git a/seamless/core/sid.py b/seamless/core/sid.py
deleted file mode 100644
index 674beea..0000000
--- a/seamless/core/sid.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from typing import NewType
-
-SocketID = NewType("SocketID", str)
diff --git a/seamless/element.py b/seamless/element.py
deleted file mode 100644
index 6ea7e8d..0000000
--- a/seamless/element.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from typing import TYPE_CHECKING, TypeVar, Generic, Unpack
-
-from .internal.utils import to_iter
-
-if TYPE_CHECKING:
-    from .types import ChildrenType, ChildType
-    from .types.html import HTMLElement
-
-    PropsType = TypeVar("PropsType", bound=HTMLElement)
-
-else:
-    PropsType = TypeVar("PropsType")
-
-
-class Element(Generic[PropsType]):
-    def __init__(
-        self,
-        *args: "ChildType",
-        children: "ChildrenType | None" = None,
-        **kwargs: Unpack[PropsType],  # type: ignore - until https://github.com/python/typing/issues/1399 is resolved
-    ):
-        self.children = tuple(to_iter(children or args))
-        self.props = kwargs
-
-    tag_name: str
-
-    inline = False
-
-    def __call__(self, *children: "ChildType"):
-        self.children = children
-        return self
diff --git a/seamless/errors.py b/seamless/errors.py
index b15cbf5..d0c7dc9 100644
--- a/seamless/errors.py
+++ b/seamless/errors.py
@@ -1,4 +1,7 @@
-class Error(Exception):
+from typing import Optional
+from pydom.errors import Error as PydomError
+
+class Error(PydomError):
     """
     Base class for all seamless exceptions.
     """
@@ -19,5 +22,6 @@ class ClientError(Error):
     Raised when an error occurs on the client side.
     Classes that inherit from this class will not be raised on the server side.
     """
-
-    ...
+    def __init__(self, *args: object, client_id: Optional[str] = None) -> None:
+        super().__init__(*args)
+        self.client_id = client_id
diff --git a/seamless/extensions/__init__.py b/seamless/extensions/__init__.py
index 1938314..0c509e7 100644
--- a/seamless/extensions/__init__.py
+++ b/seamless/extensions/__init__.py
@@ -1,5 +1,7 @@
 from .state import State
+from .transports import SocketIOTransport
 
 __all__ = [
+    "SocketIOTransport",
     "State",
 ]
diff --git a/seamless/extensions/transports.py b/seamless/extensions/transports.py
new file mode 100644
index 0000000..490e77d
--- /dev/null
+++ b/seamless/extensions/transports.py
@@ -0,0 +1,1 @@
+from ..extra.transports.socketio.transport import SocketIOTransport
diff --git a/seamless/extra/components/__init__.py b/seamless/extra/components/__init__.py
index 8f5d610..fc4914f 100644
--- a/seamless/extra/components/__init__.py
+++ b/seamless/extra/components/__init__.py
@@ -1,18 +1,23 @@
-from typing import TYPE_CHECKING
+import inspect
+from typing import TYPE_CHECKING, ClassVar, Optional, Type
 
-from ...core.component import Component
-from ...errors import ClientError
-from ...rendering import render_json
+from pydom import Component
+from pydom.context import Context
+from pydom.context.feature import Feature
+from pydom.rendering import render_json
 
-from ..feature import Feature
+from ...errors import ClientError
 from .repository import ComponentsRepository
+from ..transports.transport import TransportFeature
 
 if TYPE_CHECKING:
-    from ...context import Context
+
+    class _Component(Component):
+        __seamless_name__: ClassVar[str]
 
 
 class ComponentsFeature(Feature):
-    def __init__(self, context: "Context") -> None:
+    def __init__(self, context: Context) -> None:
         super().__init__(context)
         self.DB = ComponentsRepository()
 
@@ -20,11 +25,17 @@ def __init__(self, context: "Context") -> None:
 
         @classmethod
         def __init_subclass__(
-            cls: type[Component], *, name: str | None = None, inject_render: bool = False, **kwargs
+            cls: Type["_Component"],
+            *,
+            name: Optional[str] = None,
+            inject_render: bool = False,
+            **kwargs,
         ) -> None:
-            if cls is not Component:
+            if not inspect.isabstract(cls):
 
-                cls.__seamless_name__ = name or cls.__name__
+                cls.__seamless_name__ = (
+                    cls.__dict__.get("__seamless_name__", None) or name or cls.__name__
+                )
                 self.DB.add_component(cls, cls.__seamless_name__)
 
                 if inject_render:
@@ -34,14 +45,31 @@ def __init_subclass__(
 
         Component.__init_subclass__ = __init_subclass__
 
-        context.on("component", self.get_component)
+        try:
+            transport = context.get_feature(TransportFeature)
+        except KeyError:
+            raise ValueError(
+                "ComponentsFeature requires a TransportFeature in the context."
+                "Please add it before adding the ComponentsFeature."
+            )
+
+        transport.event.on("component", self.get_component)
 
-    def get_component(self, sid: str, name: str, props=None):
+    async def get_component(self, client_id: str, component_name: str, props=None, *_):
         props = props or {}
-        cls = self.DB.get_component(name)
+        cls = self.DB.get_component(component_name)
+
         try:
             component = cls(**props)
         except TypeError:
-            raise ClientError(f"Invalid props for component {name}")
+            raise ClientError(
+                f"Invalid props for component {component_name}", client_id=client_id
+            )
+
+        return render_json(
+            component, context=self.context, **{"transports.client_id": client_id}
+        )
+
 
-        return render_json(component, context=self.context, events_scope=sid)
+def component_name(component: Type[Component]) -> Optional[str]:
+    return getattr(component, "__seamless_name__", None)
diff --git a/seamless/extra/components/repository.py b/seamless/extra/components/repository.py
index c279a58..e2d415e 100644
--- a/seamless/extra/components/repository.py
+++ b/seamless/extra/components/repository.py
@@ -1,15 +1,14 @@
-from typing import TYPE_CHECKING
+from typing import Type
 
-if TYPE_CHECKING:
-    from ...core import Component
+from pydom import Component
 
 
 class ComponentsRepository:
     def __init__(self):
         self.components = {}
 
-    def add_component(self, component: type["Component"], name: str):
+    def add_component(self, component: Type[Component], name: str):
         self.components[name] = component
 
-    def get_component(self, component_name: str) -> "Component":
+    def get_component(self, component_name: str) -> Type[Component]:
         return self.components[component_name]
diff --git a/seamless/extra/events/__init__.py b/seamless/extra/events/__init__.py
index 82194f1..6d51a55 100644
--- a/seamless/extra/events/__init__.py
+++ b/seamless/extra/events/__init__.py
@@ -1,76 +1,97 @@
-from typing import TYPE_CHECKING, Callable
+from typing import Callable
 
-from ...internal.validation import wrap_with_validation
-
-from .database import ElementsDatabase, Action
+from pydom.context import Context
+from pydom.rendering.render_state import RenderState
+from pydom.rendering.tree.nodes import ContextNode
 
+from .database import EventsDatabase, Action
 from ..feature import Feature
-
 from ...internal.constants import (
-    CLAIM_COOKIE_NAME,
     SEAMLESS_ELEMENT_ATTRIBUTE,
     SEAMLESS_INIT_ATTRIBUTE,
 )
-from ...internal.cookies import Cookies
-from ...rendering.tree import ElementNode
-from ...rendering.render_state import RenderState
 
-if TYPE_CHECKING:
-    from ...context import Context
+from ...internal.validation import wrap_with_validation
+from ..transports.errors import TransportConnectionRefused
+from ..transports.transport import TransportFeature
 
 
 class EventsFeature(Feature):
-    def __init__(self, context: "Context", claim_time=20.0):
+    def __init__(self, context: Context):
         super().__init__(context)
-        self.DB = ElementsDatabase(claim_time=claim_time)
 
-        self.context.on("connect", self.on_connect)
-        self.context.on("disconnect", self.on_disconnect)
-        self.context.on("event", self.event)
-
-        self.context.add_prop_transformer(*self.events_transformer())
+        try:
+            self.transport = context.get_feature(TransportFeature)
+        except KeyError:
+            raise ValueError(
+                "EventsFeature requires a TransportFeature in the context."
+                "Please add it before adding the EventsFeature."
+            )
 
-    async def event(self, sid: str, data: str, event_data: dict):
-        return await self.DB.invoke_event(data, event_data, scope=sid)
+        self.DB = EventsDatabase()
+        self.context.add_prop_transformer(*self._events_transformer())
+        self.context.add_post_render_transformer(self._post_render_transformer)
 
-    async def on_connect(self, sid: str, env: dict):
-        cookies = Cookies(env.get("HTTP_COOKIE", ""))
-        claim_id = cookies[CLAIM_COOKIE_NAME]
-        if not claim_id:
-            return await self.context.server.disconnect(sid)
+        self.transport.disconnect += self._on_disconnect
+        self.transport.event.on("event", self._event)
 
-        self.DB.claim(claim_id, sid)
+    async def _event(self, client_id: str, event_id: str, *event_data):
+        return await self.DB.invoke_event(event_id, *event_data, scope=client_id)
 
-    async def on_disconnect(self, sid: str):
-        self.DB.release_actions(sid)
+    async def _on_disconnect(self, client_id: str):
+        self.DB.release_actions(client_id)
 
-    def events_transformer(self):
+    def _events_transformer(self):
 
         def matcher(key: str, value):
             return key.startswith("on_") and callable(value)
 
         def transformer(
-            key: str, value: Callable, element: ElementNode, render_state: RenderState
+            key: str,
+            value: Callable,
+            element: ContextNode,
+            render_state: RenderState,
         ):
             event_name = key.removeprefix("on").replace("_", "").lower()
-            action = self.DB.add_event(
-                Action(
-                    wrap_with_validation(self.context.inject(value), context=self.context), str(hash(value))
-                ),
-                render_state.custom_data.get("events_scope", None),
+            action = Action(
+                wrap_with_validation(self.context.inject(value), context=self.context),
+                str(hash(value)),
             )
+
+            render_state.custom_data.setdefault("events.actions", []).append(action)
+
             element.props[SEAMLESS_INIT_ATTRIBUTE] = (
                 str(element.props.get(SEAMLESS_INIT_ATTRIBUTE, ""))
-                + f"""this.addEventListener('{event_name}', (event) => {{
-              const outEvent = seamless.instance.eventObjectTransformer(
-                event, 
-                seamless.instance.serializeEventObject(event)
-              );
-            seamless.instance.socket.emit("event", "{action.id}", outEvent);
-          }});"""
+                + f"""
+                this.addEventListener('{event_name}', (event) => {{
+                    const outEvent = seamless.instance.eventObjectTransformer(
+                        event, 
+                        seamless.instance.serializeEventObject(event)
+                    );
+                    seamless.emit("event", "{action.id}", outEvent);
+                }});
+                """
             )
 
             element.props[SEAMLESS_ELEMENT_ATTRIBUTE] = True
             del element.props[key]
 
         return matcher, transformer
+
+    def _post_render_transformer(self, root: ContextNode, render_state: RenderState):
+        actions = render_state.custom_data.get("events.actions", [])
+
+        if len(actions) == 0:
+            return
+
+        client_id = render_state.custom_data.get("transports.client_id", None)
+
+        if not client_id:
+            raise ValueError(
+                "transports.client_id is not set in the custom_data. "
+                "Please make sure that the TransportFeature is added to the context "
+                "and TransportFeature.init() is called inside your component."
+            )
+        else:
+            for action in actions:
+                self.DB.add_event(action, scope=client_id)
diff --git a/seamless/extra/events/database.py b/seamless/extra/events/database.py
index 6f71391..65a7126 100644
--- a/seamless/extra/events/database.py
+++ b/seamless/extra/events/database.py
@@ -1,14 +1,9 @@
-from inspect import iscoroutinefunction, ismethod, signature
-from typing import Any, Callable, TypeAlias
-from threading import Timer
+from inspect import iscoroutinefunction
+from typing import Any, Callable, Dict
 
-from ...core import SocketID
-from .errors import ActionError
-from ...internal.validation import wrap_with_validation
-from ...context.request import request as _request, RequestType
+from ...internal.utils import is_global
 
-
-ActionsMap: TypeAlias = dict[str, Callable | dict[str, Callable]]
+from .errors import EventNotFoundError
 
 
 class Action:
@@ -26,93 +21,46 @@ async def __call__(self, *args: Any, **kwargs: Any) -> Any:
         return self.action(*args, **kwargs)
 
 
-class ElementsDatabase:
+class EventsDatabase:
 
-    def __init__(self, *, claim_time=20.0):
-        self.events: ActionsMap = {}
+    def __init__(self):
+        self.events: Dict[str, Action] = {}
+        self.scoped_events: Dict[str, Dict[str, Action]] = {}
         self.actions_ids = dict[str | Callable, Action]()
-        self._unclaimed_elements = dict[str, list[Action]]()
-        self._unclaimed_elements_timeouts = dict[str, Timer]()
-        self.claim_time = claim_time
 
-    def add_event(self, action: Action, scope=None):
+    def add_event(self, action: Action, *, scope: str):
         try:
             return self.actions_ids[action.id]
         except KeyError:
             pass
 
-        request = _request()
-
-        if request.type == RequestType.HTTP:
-            if request.id not in self._unclaimed_elements:
-                self._unclaimed_elements[request.id] = []
-
-            self._unclaimed_elements[request.id].append(action)
-
-            def delete_unclaimed(claim_id):
-                if claim_id in self._unclaimed_elements:
-                    del self._unclaimed_elements[claim_id]
+        self.actions_ids[action.id] = action
 
-            timer = Timer(self.claim_time, delete_unclaimed, [request.id])
-            self._unclaimed_elements_timeouts[request.id] = timer
-            timer.start()
-        else:
-            self.actions_ids[action.id] = action
+        if is_global(action.action):
+            self.events[action.id] = action
 
-            if scope:
-                if scope not in self.events:
-                    self.events[scope] = {}
-
-                self.events[scope][action.id] = action
-            else:
-                self.events[action.id] = action
+        self.scoped_events.setdefault(scope, {})[action.id] = action
 
         return action
 
-    async def invoke_event(self, event: str, *data, scope=None):
-        if scope and scope in self.events and event in self.events[scope]:
-            func = self.events[scope][event]
-        elif event in self.events:
-            func = self.events[event]
-        else:
-            raise ActionError("Event not found")
-
-        data = list(data)
-        for index, param in enumerate(signature(func.action).parameters.values()):
-            if param.annotation is SocketID:
-                data.insert(index, scope)
-        return await func(*data)
-
-    def claim(self, claim_id, client_id):
-        if claim_id not in self._unclaimed_elements:
-            return
-
-        self._unclaimed_elements_timeouts[claim_id].cancel()
-        del self._unclaimed_elements_timeouts[claim_id]
-
-        for action in self._unclaimed_elements[claim_id]:
-            if ismethod(action.action):
-                if client_id not in self.events:
-                    self.events[client_id] = {}
-                self.events[client_id][action.id] = action
-            else:
-                self.events[action.id] = action
+    async def invoke_event(self, event: str, *data, scope: str):
+        try:
+            func = self.get_event(event, scope=scope)
+        except KeyError:
+            raise EventNotFoundError(event)
 
-        del self._unclaimed_elements[claim_id]
+        return await func(*data)
 
-    def release_actions(self, socket_id: str):
-        if socket_id not in self.events:
+    def release_actions(self, client_id: str):
+        if client_id not in self.scoped_events:
             return
 
-        del self.events[socket_id]
-
-    @property
-    def _all_unclaimed(self):
-        all_unclaimed = {}
-        for subscriptable_list in self._unclaimed_elements.values():
-            all_unclaimed.update((e.id, e) for e in subscriptable_list)
+        del self.scoped_events[client_id]
 
-        return all_unclaimed
+    def get_event(self, event_id: str, *, scope: str):
+        if event_id in self.events:
+            return self.events[event_id]
+        
+        return self.scoped_events[scope][event_id]
 
 
-DB = ElementsDatabase(claim_time=30)
diff --git a/seamless/extra/events/errors.py b/seamless/extra/events/errors.py
index 6740047..03827a8 100644
--- a/seamless/extra/events/errors.py
+++ b/seamless/extra/events/errors.py
@@ -1,7 +1,14 @@
-from ...errors import Error
+from ...errors import Error, ClientError
 
 class ActionError(Error):
     """
     Raised when an error occurs while invoking an action.
     """
+    ...
+
+
+class EventNotFoundError(ClientError):
+    """
+    Raised when an event is not found.
+    """
     ...
\ No newline at end of file
diff --git a/seamless/extra/feature.py b/seamless/extra/feature.py
index a065017..0ac0d63 100644
--- a/seamless/extra/feature.py
+++ b/seamless/extra/feature.py
@@ -1,9 +1,1 @@
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from ..context import Context
-
-
-class Feature:
-    def __init__(self, context: "Context") -> None:
-        self.context = context
+from pydom.context.feature import Feature
diff --git a/seamless/extra/state/__init__.py b/seamless/extra/state/__init__.py
index 178d44b..a0a2e25 100644
--- a/seamless/extra/state/__init__.py
+++ b/seamless/extra/state/__init__.py
@@ -1,16 +1,14 @@
-from pathlib import Path
 from json import dumps
-from typing import Any, overload, TYPE_CHECKING
+from pathlib import Path
+from typing import Any, overload
 
-from ...core.component import Component
-from ...core import Empty, JS
-from ...internal.constants import SEAMLESS_ELEMENT_ATTRIBUTE, SEAMLESS_INIT_ATTRIBUTE
-from ...rendering.tree import ElementNode
+from pydom import Component
+from pydom.context import Context
+from pydom.rendering.tree.nodes import ContextNode
 
+from ...core import Empty, JS
 from ..feature import Feature
-
-if TYPE_CHECKING:
-    from ...context import Context
+from ...internal.constants import SEAMLESS_ELEMENT_ATTRIBUTE, SEAMLESS_INIT_ATTRIBUTE
 
 HERE = Path(__file__).parent
 _EMPTY = object()
@@ -20,9 +18,7 @@ class _StateMeta(type):
     _instances = {}
 
     def __call__(self, name: str, *args: Any, **kwds: Any):
-        if name not in self._instances:
-            self._instances[name] = super().__call__(name, *args, **kwds)
-        return self._instances[name]
+        return self._instances.setdefault(name, super().__call__(name, *args, **kwds))
 
     def __iter__(self):
         return iter(self._instances.values())
@@ -76,7 +72,7 @@ def render(self):
 
 
 class StateFeature(Feature):
-    def __init__(self, context: "Context"):
+    def __init__(self, context: Context):
         super().__init__(context)
         self.context.add_prop_transformer(*self.state_transformer())
 
@@ -84,7 +80,7 @@ def state_transformer(self):
         def matcher(_, value):
             return isinstance(value, Empty) and value.props.get("state-name", False)
 
-        def transformer(key, value: Empty, element: ElementNode):
+        def transformer(key, value: Empty, element: ContextNode):
             empty_props = value.props
             element.props[SEAMLESS_ELEMENT_ATTRIBUTE] = True
             element.props[SEAMLESS_INIT_ATTRIBUTE] = (
diff --git a/seamless/extra/transformers/class_transformer.py b/seamless/extra/transformers/class_transformer.py
deleted file mode 100644
index 944930f..0000000
--- a/seamless/extra/transformers/class_transformer.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def class_transformer():
-    def matcher(key, _):
-        return key == "class_name"
-
-    def transformer(_, class_name, element):
-        if not isinstance(class_name, str):
-            class_name = " ".join(class_name)
-
-        element.props["class"] = " ".join(str(class_name).split())
-        del element.props["class_name"]
-
-    return matcher, transformer
\ No newline at end of file
diff --git a/seamless/extra/transformers/dash_transformer.py b/seamless/extra/transformers/dash_transformer.py
deleted file mode 100644
index 4d45295..0000000
--- a/seamless/extra/transformers/dash_transformer.py
+++ /dev/null
@@ -1,10 +0,0 @@
-def dash_transformer():
-    def matcher(key: str, value):
-        return "_" in key and (isinstance(value, (str, int, float)) or value is None)
-    
-    def transformer(key: str, value, element):
-        dash_key = key.replace("_", "-")
-        element.props[dash_key] = value
-        del element.props[key]
-
-    return matcher, transformer
\ No newline at end of file
diff --git a/seamless/extra/transformers/html_events_transformer.py b/seamless/extra/transformers/html_events_transformer.py
deleted file mode 100644
index 918719e..0000000
--- a/seamless/extra/transformers/html_events_transformer.py
+++ /dev/null
@@ -1,10 +0,0 @@
-def html_events_transformer():
-    def matcher(key: str, value):
-        return key.startswith("on_") and isinstance(value, str)
-
-    def transformer(key: str, value, element):       
-        event_name = key.replace("_", "").lower()
-        element.props[event_name] = value
-        del element.props[key]
-
-    return matcher, transformer
\ No newline at end of file
diff --git a/seamless/extra/transformers/js_transformer.py b/seamless/extra/transformers/js_transformer.py
index 74c0324..86c0905 100644
--- a/seamless/extra/transformers/js_transformer.py
+++ b/seamless/extra/transformers/js_transformer.py
@@ -1,5 +1,9 @@
 from ...core import JavaScript
-from ...internal.constants import SEAMLESS_ELEMENT_ATTRIBUTE, SEAMLESS_INIT_ATTRIBUTE
+from ...internal.constants import (
+    SEAMLESS_ELEMENT_ATTRIBUTE,
+    SEAMLESS_INIT_ATTRIBUTE,
+    SEAMLESS_INIT_ASYNC_ATTRIBUTE,
+)
 
 
 def init_transformer():
@@ -11,6 +15,8 @@ def transformer(key: str, source: JavaScript, element):
         element.props[SEAMLESS_INIT_ATTRIBUTE] = (
             element.props.get(SEAMLESS_INIT_ATTRIBUTE, "") + source.code
         )
+        if source.async_:
+            element.props[SEAMLESS_INIT_ASYNC_ATTRIBUTE] = True
         del element.props[key]
 
     return matcher, transformer
@@ -26,7 +32,7 @@ def transformer(key: str, source: JavaScript, element):
         element.props[SEAMLESS_ELEMENT_ATTRIBUTE] = True
         element.props[SEAMLESS_INIT_ATTRIBUTE] = (
             element.props.get(SEAMLESS_INIT_ATTRIBUTE, "")
-            + f"\nthis.addEventListener('{event_name}', (event) => {{{source.code}}});"
+            + f"\nthis.addEventListener('{event_name}', {'async' if source.async_ else ''}(event) => {{{source.code}}});"
         )
         del element.props[key]
 
diff --git a/seamless/extra/transformers/simple_transformer.py b/seamless/extra/transformers/simple_transformer.py
deleted file mode 100644
index 65c17b4..0000000
--- a/seamless/extra/transformers/simple_transformer.py
+++ /dev/null
@@ -1,24 +0,0 @@
-_SIMPLE_TRANSFORMERS = {
-    "html_for": "for",
-    "accept_charset": "accept-charset",
-    "http_equiv": "http-equiv",
-    "access_key": "accesskey",
-    "content_editable": "contenteditable",
-    "cross_origin": "crossorigin",
-    "tab_index": "tabindex",
-    "use_map": "usemap",
-    "col_span": "colspan",
-    "row_span": "rowspan",
-    "char_set": "charset",
-}
-
-
-def simple_transformer():
-    def matcher(key: str, _):
-        return key in _SIMPLE_TRANSFORMERS
-
-    def transformer(key: str, value, element):
-        element.props[_SIMPLE_TRANSFORMERS[key]] = value
-        del element.props[key]
-
-    return matcher, transformer
diff --git a/seamless/extra/transformers/style_transformer.py b/seamless/extra/transformers/style_transformer.py
deleted file mode 100644
index 7d744a7..0000000
--- a/seamless/extra/transformers/style_transformer.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from ...styling import StyleObject
-
-def style_transformer():
-    def matcher(_, value):
-        return isinstance(value, StyleObject)
-    
-    def transformer(key: str, value: StyleObject, element):
-        element.props[key] = value.to_css()
-    
-    return matcher, transformer
diff --git a/seamless/extra/transports/client_id.py b/seamless/extra/transports/client_id.py
new file mode 100644
index 0000000..c7cc400
--- /dev/null
+++ b/seamless/extra/transports/client_id.py
@@ -0,0 +1,3 @@
+from typing import NewType
+
+ClientID = NewType("ClientID", str)
diff --git a/seamless/extra/transports/dispatcher.py b/seamless/extra/transports/dispatcher.py
new file mode 100644
index 0000000..515851f
--- /dev/null
+++ b/seamless/extra/transports/dispatcher.py
@@ -0,0 +1,41 @@
+from typing import Any, Callable, Dict, Generic, TYPE_CHECKING, TypeVar, Awaitable
+
+from pydom import Context
+from typing_extensions import ParamSpec
+
+
+if TYPE_CHECKING:
+    from .transport import TransportFeature
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+
+class Dispatcher(Generic[P, T]):
+    def __init__(self, context: Context) -> None:
+        self._context = context
+        self._handlers: Dict[Any, Callable] = {}
+
+    def on(self, name: Any, handler: Callable[P, Awaitable[T]]):
+        if name in self._handlers:
+            raise ValueError(f"Handler for {name} already exists")
+        
+        self._handlers[name] = self._context.inject(handler)
+
+    async def __call__(self, name: Any, *args: P.args, **kwds: P.kwargs) -> Awaitable[T]:
+        return await self._handlers[name](*args, **kwds)
+
+
+class DispatcherDescriptor(Generic[P, T]):
+    def __get__(self, instance: "TransportFeature", owner) -> Dispatcher[P, T]:
+        return instance.__dict__.setdefault(self.name, Dispatcher(instance.context))
+
+    def __set__(self, instance: "TransportFeature", value: Dispatcher[P, T]) -> None:
+        pass
+
+    def __set_name__(self, owner, name):
+        self.name = name
+
+
+def dispatcher(_: Callable[P, T]) -> DispatcherDescriptor[P, T]:
+    return DispatcherDescriptor()
diff --git a/seamless/extra/transports/errors.py b/seamless/extra/transports/errors.py
new file mode 100644
index 0000000..5e3dfc7
--- /dev/null
+++ b/seamless/extra/transports/errors.py
@@ -0,0 +1,8 @@
+from ...errors import ClientError
+
+
+class TransportConnectionRefused(ClientError):
+    """
+    Raised when the connection to the transport is refused.
+    """
+    pass
diff --git a/seamless/extra/transports/socketio/middleware.py b/seamless/extra/transports/socketio/middleware.py
new file mode 100644
index 0000000..e396d86
--- /dev/null
+++ b/seamless/extra/transports/socketio/middleware.py
@@ -0,0 +1,26 @@
+from typing import Optional
+from socketio import ASGIApp
+
+from pydom.context import Context, get_context
+
+from .transport import SocketIOTransport
+
+
+class SocketIOMiddleware:
+    def __init__(
+        self, app, socket_path="/socket.io", context: Optional[Context] = None
+    ):
+        context = get_context(context)
+        self.socket_path = f"/{socket_path.strip('/')}"
+        try:
+            transport = context.get_feature(SocketIOTransport)
+        except KeyError:
+            raise ValueError(
+                "SocketIOTransport feature is required when using SocketIOMiddleware. "
+                "Please add it to the context."
+            )
+
+        self.app = ASGIApp(transport.server, app, socketio_path=self.socket_path)
+
+    async def __call__(self, scope, receive, send):
+        await self.app(scope, receive, send)
diff --git a/seamless/extra/transports/socketio/socketio.init.js b/seamless/extra/transports/socketio/socketio.init.js
new file mode 100644
index 0000000..f86a2d7
--- /dev/null
+++ b/seamless/extra/transports/socketio/socketio.init.js
@@ -0,0 +1,26 @@
+await import("https://cdn.socket.io/4.7.5/socket.io.js");
+const socket = io(socketIOConfig);
+
+seamless.emit = (event, ...data) => {
+  socket.emit(event, ...data);
+};
+
+seamless.registerEventListener = (event, callback) => {
+  socket.on(event, callback);
+};
+
+seamless.sendWaitResponse = (event, ...args) => {
+  return new Promise((resolve) => {
+    socket.emit(event, ...args, resolve);
+  });
+};
+
+seamless.getComponent = async (name, props = {}) => {
+  return await seamless.sendWaitResponse("component", name, props);
+};
+
+window.dispatchEvent(
+  new CustomEvent("transportsAvailable", {
+    detail: { clientId: socketIOConfig.query.client_id },
+  })
+);
diff --git a/seamless/extra/transports/socketio/transport.py b/seamless/extra/transports/socketio/transport.py
new file mode 100644
index 0000000..7af9e79
--- /dev/null
+++ b/seamless/extra/transports/socketio/transport.py
@@ -0,0 +1,74 @@
+from json import dumps
+from pathlib import Path
+from urllib.parse import parse_qs
+
+from pydom import Component, Context
+from pydom.rendering.render_state import RenderState
+from pydom.utils.functions import random_string
+from socketio import AsyncServer
+
+from ....core.javascript import JS
+from ....core.empty import Empty
+from ..client_id import ClientID
+from ..errors import TransportConnectionRefused
+from ..transport import TransportFeature
+
+HERE = Path(__file__).parent
+
+
+class SocketIOTransport(TransportFeature):
+    def __init__(self, context: Context):
+        super().__init__(context)
+
+        self.server = AsyncServer(async_mode="asgi")
+
+        self.server.on("connect", self.on_connect)
+        self.server.on("*", self.on_event)
+        self.server.on("disconnect", self.on_disconnect)
+
+    async def on_connect(self, sid, env):
+        try:
+            query = parse_qs(env.get("QUERY_STRING", ""))
+            client_id = query.get("client_id", [None])[0]
+            await self.connect(client_id, env)
+            await self.server.save_session(sid, {"client_id": client_id})
+        except TransportConnectionRefused:
+            await self.server.disconnect(sid)
+
+    async def on_event(self, event_name, sid, *data):
+        client_id = await self._client_id(sid)
+        with self.context.injector.scope({ClientID: lambda: client_id}):
+            res = await self.event(event_name, client_id, *data)
+            return res
+
+    async def on_disconnect(self, sid):
+        client_id = await self._client_id(sid)
+        await self.disconnect(client_id)
+
+    async def _client_id(self, sid):
+        session = await self.server.get_session(sid)
+        return session["client_id"]
+
+    @staticmethod
+    def init(config=None):
+        init_js = JS(file=HERE / "socketio.init.js", async_=True)
+
+        class InitSocketIO(Component, inject_render=True):
+            def render(self, render_state: RenderState):
+                client_id = render_state.custom_data.setdefault(
+                    "transports.client_id", random_string(24)
+                )
+
+                socket_options = config or {}
+                socket_options.setdefault("query", {})["client_id"] = client_id
+
+                return Empty(
+                    init=JS(
+                        f"""
+                        const socketIOConfig = {dumps(socket_options)};
+                        """
+                    )
+                    + init_js
+                )
+
+        return InitSocketIO()
diff --git a/seamless/extra/transports/subscriptable.py b/seamless/extra/transports/subscriptable.py
new file mode 100644
index 0000000..211d084
--- /dev/null
+++ b/seamless/extra/transports/subscriptable.py
@@ -0,0 +1,45 @@
+from typing import Any, Generic, Callable
+
+from pydom import Context
+from pydom.context.feature import Feature
+from typing_extensions import ParamSpec
+
+from ...errors import ClientError
+
+
+P = ParamSpec("P")
+
+
+class Subscriptable(Generic[P]):
+    def __init__(self, context: Context) -> None:
+        self._callbacks = []
+        self._context = context
+
+    async def __call__(self, *args: P.args, **kwds: P.kwargs) -> None:
+        try:
+            for callback in self._callbacks:
+                await callback(*args, **kwds)
+        except ClientError as error:
+            from .transport import TransportFeature
+
+            transport = self._context.get_feature(TransportFeature)
+            await transport.error(error)
+
+    def __iadd__(self, callback: Any) -> "Subscriptable":
+        self._callbacks.append(self._context.inject(callback))
+        return self
+
+
+class SubscriptableDescriptor(Generic[P]):
+    def __get__(self, instance: "Feature", owner) -> Subscriptable[P]:
+        return instance.__dict__.setdefault(self.name, Subscriptable(instance.context))
+
+    def __set__(self, instance: "Feature", value: Subscriptable[P]) -> None:
+        pass
+
+    def __set_name__(self, owner, name):
+        self.name = name
+
+
+def event(_: Callable[P, None]) -> SubscriptableDescriptor[P]:
+    return SubscriptableDescriptor()
diff --git a/seamless/extra/transports/transport.py b/seamless/extra/transports/transport.py
new file mode 100644
index 0000000..6430313
--- /dev/null
+++ b/seamless/extra/transports/transport.py
@@ -0,0 +1,32 @@
+from typing import Any
+
+from pydom.context import Context
+
+from ..feature import Feature
+from .dispatcher import dispatcher
+from .subscriptable import event
+
+
+class TransportFeature(Feature):
+    def __init__(self, context: Context) -> None:
+        super().__init__(context)
+
+    @event
+    @staticmethod
+    def connect(client_id: str, request: Any) -> None:
+        pass
+
+    @event
+    @staticmethod
+    def disconnect(client_id: str) -> None:
+        pass
+
+    @event
+    @staticmethod
+    def error(error: Exception) -> None:
+        pass
+
+    @dispatcher
+    @staticmethod
+    def event(client_id: str, /, *args: Any) -> Any:
+        pass
diff --git a/seamless/html/__init__.py b/seamless/html/__init__.py
index bd79164..99a419f 100644
--- a/seamless/html/__init__.py
+++ b/seamless/html/__init__.py
@@ -1,233 +1,2 @@
-from .a import A
-from .abbr import Abbr
-from .address import Address
-from .area import Area
-from .article import Article
-from .aside import Aside
-from .audio import Audio
-from .b import B
-from .base import Base
-from .bdi import Bdi
-from .bdo import Bdo
-from .blockquote import BlockQuote
-from .body import Body
-from .br import Br
-from .button import Button
-from .canvas import Canvas
-from .caption import Caption
-from .cite import Cite
-from .code import Code
-from .col import Col
-from .colgroup import ColGroup
-from .data import Data
-from .datalist import DataList
-from .dd import Dd
-from .del_ import Del
-from .details import Details
-from .dfn import Dfn
-from .dialog import Dialog
-from .div import Div
-from .dl import Dl
-from .dt import Dt
-from .em import Em
-from .embed import Embed
-from .fieldset import FieldSet
-from .figcaption import FigCaption
-from .figure import Figure
-from .footer import Footer
-from .form import Form
-from .fragment import Fragment
-from .h1 import H1
-from .h2 import H2
-from .h3 import H3
-from .h4 import H4
-from .h5 import H5
-from .h6 import H6
-from .head import Head
-from .header import Header
-from .hgroup import HGroup
-from .hr import Hr
-from .html import Html
-from .i import I
-from .iframe import IFrame
-from .img import Img
-from .input import Input
-from .ins import Ins
-from .kbd import Kbd
-from .label import Label
-from .legend import Legend
-from .li import Li
-from .link import Link
-from .main import Main
-from .map import Map
-from .mark import Mark
-from .menu import Menu
-from .meta import Meta
-from .meter import Meter
-from .nav import Nav
-from .noscript import NoScript
-from .object import Object
-from .ol import Ol
-from .optgroup import OptGroup
-from .option import Option
-from .output import Output
-from .p import P
-from .param import Param
-from .picture import Picture
-from .pre import Pre
-from .progress import Progress
-from .q import Q
-from .rp import Rp
-from .rt import Rt
-from .ruby import Ruby
-from .s import S
-from .samp import Samp
-from .script import Script
-from .search import Search
-from .section import Section
-from .select import Select
-from .slot import Slot
-from .small import Small
-from .source import Source
-from .span import Span
-from .strong import Strong
-from .style import Style
-from .sub import Sub
-from .summary import Summary
-from .sup import Sup
-from .svg import Svg
-from .table import Table
-from .tbody import TBody
-from .td import Td
-from .template import Template
-from .textarea import TextArea
-from .tfoot import TFoot
-from .th import Th
-from .thead import THead
-from .time import Time
-from .title import Title
-from .tr import Tr
-from .track import Track
-from .u import U
-from .ul import Ul
-from .var import Var
-from .video import Video
-from .wbr import Wbr
+from pydom.html import *
 
-__all__ = [
-	"A",
-	"Abbr",
-	"Address",
-	"Area",
-	"Article",
-	"Aside",
-	"Audio",
-	"B",
-	"Base",
-	"Bdi",
-	"Bdo",
-	"BlockQuote",
-	"Body",
-	"Br",
-	"Button",
-	"Canvas",
-	"Caption",
-	"Cite",
-	"Code",
-	"Col",
-	"ColGroup",
-	"Data",
-	"DataList",
-	"Dd",
-	"Del",
-	"Details",
-	"Dfn",
-	"Dialog",
-	"Div",
-	"Dl",
-	"Dt",
-	"Em",
-	"Embed",
-	"FieldSet",
-	"FigCaption",
-	"Figure",
-	"Footer",
-	"Form",
-	"Fragment",
-	"H1",
-	"H2",
-	"H3",
-	"H4",
-	"H5",
-	"H6",
-	"Head",
-	"Header",
-	"HGroup",
-	"Hr",
-	"Html",
-	"I",
-	"IFrame",
-	"Img",
-	"Input",
-	"Ins",
-	"Kbd",
-	"Label",
-	"Legend",
-	"Li",
-	"Link",
-	"Main",
-	"Map",
-	"Mark",
-	"Menu",
-	"Meta",
-	"Meter",
-	"Nav",
-	"NoScript",
-	"Object",
-	"Ol",
-	"OptGroup",
-	"Option",
-	"Output",
-	"P",
-	"Param",
-	"Picture",
-	"Pre",
-	"Progress",
-	"Q",
-	"Rp",
-	"Rt",
-	"Ruby",
-	"S",
-	"Samp",
-	"Script",
-	"Search",
-	"Section",
-	"Select",
-	"Slot",
-	"Small",
-	"Source",
-	"Span",
-	"Strong",
-	"Style",
-	"Sub",
-	"Summary",
-	"Sup",
-	"Svg",
-	"Table",
-	"TBody",
-	"Td",
-	"Template",
-	"TextArea",
-	"TFoot",
-	"Th",
-	"THead",
-	"Time",
-	"Title",
-	"Tr",
-	"Track",
-	"U",
-	"Ul",
-	"Var",
-	"Video",
-	"Wbr",
-]
diff --git a/seamless/html/__init__.pyi b/seamless/html/__init__.pyi
new file mode 100644
index 0000000..f33e5ac
--- /dev/null
+++ b/seamless/html/__init__.pyi
@@ -0,0 +1,233 @@
+from .a import A
+from .abbr import Abbr
+from .address import Address
+from .area import Area
+from .article import Article
+from .aside import Aside
+from .audio import Audio
+from .b import B
+from .base import Base
+from .bdi import Bdi
+from .bdo import Bdo
+from .blockquote import BlockQuote
+from .body import Body
+from .br import Br
+from .button import Button
+from .canvas import Canvas
+from .caption import Caption
+from .cite import Cite
+from .code import Code
+from .col import Col
+from .colgroup import ColGroup
+from .data import Data
+from .datalist import DataList
+from .dd import Dd
+from .del_ import Del
+from .details import Details
+from .dfn import Dfn
+from .dialog import Dialog
+from .div import Div
+from .dl import Dl
+from .dt import Dt
+from .em import Em
+from .embed import Embed
+from .fieldset import FieldSet
+from .figcaption import FigCaption
+from .figure import Figure
+from .footer import Footer
+from .form import Form
+from .fragment import Fragment
+from .h1 import H1
+from .h2 import H2
+from .h3 import H3
+from .h4 import H4
+from .h5 import H5
+from .h6 import H6
+from .head import Head
+from .header import Header
+from .hgroup import HGroup
+from .hr import Hr
+from .html import Html
+from .i import I
+from .iframe import IFrame
+from .img import Img
+from .input import Input
+from .ins import Ins
+from .kbd import Kbd
+from .label import Label
+from .legend import Legend
+from .li import Li
+from .link import Link
+from .main import Main
+from .map import Map
+from .mark import Mark
+from .menu import Menu
+from .meta import Meta
+from .meter import Meter
+from .nav import Nav
+from .noscript import NoScript
+from .object import Object
+from .ol import Ol
+from .optgroup import OptGroup
+from .option import Option
+from .output import Output
+from .p import P
+from .param import Param
+from .picture import Picture
+from .pre import Pre
+from .progress import Progress
+from .q import Q
+from .rp import Rp
+from .rt import Rt
+from .ruby import Ruby
+from .s import S
+from .samp import Samp
+from .script import Script
+from .search import Search
+from .section import Section
+from .select import Select
+from .slot import Slot
+from .small import Small
+from .source import Source
+from .span import Span
+from .strong import Strong
+from .style import Style
+from .sub import Sub
+from .summary import Summary
+from .sup import Sup
+from .svg import Svg
+from .table import Table
+from .tbody import TBody
+from .td import Td
+from .template import Template
+from .textarea import TextArea
+from .tfoot import TFoot
+from .th import Th
+from .thead import THead
+from .time import Time
+from .title import Title
+from .tr import Tr
+from .track import Track
+from .u import U
+from .ul import Ul
+from .var import Var
+from .video import Video
+from .wbr import Wbr
+
+__all__ = [
+	"A",
+	"Abbr",
+	"Address",
+	"Area",
+	"Article",
+	"Aside",
+	"Audio",
+	"B",
+	"Base",
+	"Bdi",
+	"Bdo",
+	"BlockQuote",
+	"Body",
+	"Br",
+	"Button",
+	"Canvas",
+	"Caption",
+	"Cite",
+	"Code",
+	"Col",
+	"ColGroup",
+	"Data",
+	"DataList",
+	"Dd",
+	"Del",
+	"Details",
+	"Dfn",
+	"Dialog",
+	"Div",
+	"Dl",
+	"Dt",
+	"Em",
+	"Embed",
+	"FieldSet",
+	"FigCaption",
+	"Figure",
+	"Footer",
+	"Form",
+	"Fragment",
+	"H1",
+	"H2",
+	"H3",
+	"H4",
+	"H5",
+	"H6",
+	"Head",
+	"Header",
+	"HGroup",
+	"Hr",
+	"Html",
+	"I",
+	"IFrame",
+	"Img",
+	"Input",
+	"Ins",
+	"Kbd",
+	"Label",
+	"Legend",
+	"Li",
+	"Link",
+	"Main",
+	"Map",
+	"Mark",
+	"Menu",
+	"Meta",
+	"Meter",
+	"Nav",
+	"NoScript",
+	"Object",
+	"Ol",
+	"OptGroup",
+	"Option",
+	"Output",
+	"P",
+	"Param",
+	"Picture",
+	"Pre",
+	"Progress",
+	"Q",
+	"Rp",
+	"Rt",
+	"Ruby",
+	"S",
+	"Samp",
+	"Script",
+	"Search",
+	"Section",
+	"Select",
+	"Slot",
+	"Small",
+	"Source",
+	"Span",
+	"Strong",
+	"Style",
+	"Sub",
+	"Summary",
+	"Sup",
+	"Svg",
+	"Table",
+	"TBody",
+	"Td",
+	"Template",
+	"TextArea",
+	"TFoot",
+	"Th",
+	"THead",
+	"Time",
+	"Title",
+	"Tr",
+	"Track",
+	"U",
+	"Ul",
+	"Var",
+	"Video",
+	"Wbr",
+]
diff --git a/seamless/html/a.py b/seamless/html/a.py
deleted file mode 100644
index 7aed6d8..0000000
--- a/seamless/html/a.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLAnchorElement
-    from ..types import ChildType
-
-class A(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLAnchorElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "a"
diff --git a/seamless/html/a.pyi b/seamless/html/a.pyi
new file mode 100644
index 0000000..4ff9624
--- /dev/null
+++ b/seamless/html/a.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLAnchorElement
+from ..types import ChildType
+
+
+class A(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLAnchorElement]): ...
diff --git a/seamless/html/abbr.py b/seamless/html/abbr.py
deleted file mode 100644
index b6a61f0..0000000
--- a/seamless/html/abbr.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Abbr(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "abbr"
diff --git a/seamless/html/abbr.pyi b/seamless/html/abbr.pyi
new file mode 100644
index 0000000..3dfd9c9
--- /dev/null
+++ b/seamless/html/abbr.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Abbr(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/address.py b/seamless/html/address.py
deleted file mode 100644
index ac1f14a..0000000
--- a/seamless/html/address.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Address(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "address"
diff --git a/seamless/html/address.pyi b/seamless/html/address.pyi
new file mode 100644
index 0000000..1089cd2
--- /dev/null
+++ b/seamless/html/address.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Address(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/area.py b/seamless/html/area.py
deleted file mode 100644
index 649e0af..0000000
--- a/seamless/html/area.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLAreaElement
-    from ..types import ChildType
-
-class Area(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLAreaElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "area"
-    inline = True
diff --git a/seamless/html/area.pyi b/seamless/html/area.pyi
new file mode 100644
index 0000000..829ba11
--- /dev/null
+++ b/seamless/html/area.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLAreaElement
+from ..types import ChildType
+
+
+class Area(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLAreaElement]): ...
diff --git a/seamless/html/article.py b/seamless/html/article.py
deleted file mode 100644
index 5c0a0ff..0000000
--- a/seamless/html/article.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Article(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "article"
diff --git a/seamless/html/article.pyi b/seamless/html/article.pyi
new file mode 100644
index 0000000..fe14b57
--- /dev/null
+++ b/seamless/html/article.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Article(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/aside.py b/seamless/html/aside.py
deleted file mode 100644
index e0253e1..0000000
--- a/seamless/html/aside.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Aside(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "aside"
diff --git a/seamless/html/aside.pyi b/seamless/html/aside.pyi
new file mode 100644
index 0000000..8d414db
--- /dev/null
+++ b/seamless/html/aside.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Aside(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/audio.py b/seamless/html/audio.py
deleted file mode 100644
index be2edbc..0000000
--- a/seamless/html/audio.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLAudioElement
-    from ..types import ChildType
-
-class Audio(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLAudioElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "audio"
diff --git a/seamless/html/audio.pyi b/seamless/html/audio.pyi
new file mode 100644
index 0000000..1c4e2ce
--- /dev/null
+++ b/seamless/html/audio.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLAudioElement
+from ..types import ChildType
+
+
+class Audio(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLAudioElement]): ...
diff --git a/seamless/html/b.py b/seamless/html/b.py
deleted file mode 100644
index 0cb8b65..0000000
--- a/seamless/html/b.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class B(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "b"
diff --git a/seamless/html/b.pyi b/seamless/html/b.pyi
new file mode 100644
index 0000000..28a5db0
--- /dev/null
+++ b/seamless/html/b.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class B(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/base.py b/seamless/html/base.py
deleted file mode 100644
index e232aee..0000000
--- a/seamless/html/base.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLBaseElement
-    from ..types import ChildType
-
-class Base(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLBaseElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "base"
-    inline = True
diff --git a/seamless/html/base.pyi b/seamless/html/base.pyi
new file mode 100644
index 0000000..6e32322
--- /dev/null
+++ b/seamless/html/base.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLBaseElement
+from ..types import ChildType
+
+
+class Base(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLBaseElement]): ...
diff --git a/seamless/html/bdi.py b/seamless/html/bdi.py
deleted file mode 100644
index c61311b..0000000
--- a/seamless/html/bdi.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Bdi(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "bdi"
diff --git a/seamless/html/bdi.pyi b/seamless/html/bdi.pyi
new file mode 100644
index 0000000..e265ab6
--- /dev/null
+++ b/seamless/html/bdi.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Bdi(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/bdo.py b/seamless/html/bdo.py
deleted file mode 100644
index 785031d..0000000
--- a/seamless/html/bdo.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Bdo(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "bdo"
diff --git a/seamless/html/bdo.pyi b/seamless/html/bdo.pyi
new file mode 100644
index 0000000..c015a34
--- /dev/null
+++ b/seamless/html/bdo.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Bdo(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/blockquote.py b/seamless/html/blockquote.py
deleted file mode 100644
index 01222e0..0000000
--- a/seamless/html/blockquote.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class BlockQuote(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "blockquote"
diff --git a/seamless/html/blockquote.pyi b/seamless/html/blockquote.pyi
new file mode 100644
index 0000000..eb18f6d
--- /dev/null
+++ b/seamless/html/blockquote.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class BlockQuote(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/body.py b/seamless/html/body.py
deleted file mode 100644
index b0362fe..0000000
--- a/seamless/html/body.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLBodyElement
-    from ..types import ChildType
-
-class Body(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLBodyElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "body"
diff --git a/seamless/html/body.pyi b/seamless/html/body.pyi
new file mode 100644
index 0000000..de357a5
--- /dev/null
+++ b/seamless/html/body.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLBodyElement
+from ..types import ChildType
+
+
+class Body(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLBodyElement]): ...
diff --git a/seamless/html/br.py b/seamless/html/br.py
deleted file mode 100644
index 0e4c9e6..0000000
--- a/seamless/html/br.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLBRElement
-    from ..types import ChildType
-
-class Br(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLBRElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "br"
-    inline = True
diff --git a/seamless/html/br.pyi b/seamless/html/br.pyi
new file mode 100644
index 0000000..56aab7d
--- /dev/null
+++ b/seamless/html/br.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLBRElement
+from ..types import ChildType
+
+
+class Br(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLBRElement]): ...
diff --git a/seamless/html/button.py b/seamless/html/button.py
deleted file mode 100644
index b28f577..0000000
--- a/seamless/html/button.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLButtonElement
-    from ..types import ChildType
-
-class Button(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLButtonElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "button"
diff --git a/seamless/html/button.pyi b/seamless/html/button.pyi
new file mode 100644
index 0000000..f05841b
--- /dev/null
+++ b/seamless/html/button.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLButtonElement
+from ..types import ChildType
+
+
+class Button(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLButtonElement]): ...
diff --git a/seamless/html/canvas.py b/seamless/html/canvas.py
deleted file mode 100644
index ce360da..0000000
--- a/seamless/html/canvas.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLCanvasElement
-    from ..types import ChildType
-
-class Canvas(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLCanvasElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "canvas"
diff --git a/seamless/html/canvas.pyi b/seamless/html/canvas.pyi
new file mode 100644
index 0000000..1404cd4
--- /dev/null
+++ b/seamless/html/canvas.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLCanvasElement
+from ..types import ChildType
+
+
+class Canvas(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLCanvasElement]): ...
diff --git a/seamless/html/caption.py b/seamless/html/caption.py
deleted file mode 100644
index 2e2b259..0000000
--- a/seamless/html/caption.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableCaptionElement
-    from ..types import ChildType
-
-class Caption(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableCaptionElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "caption"
diff --git a/seamless/html/caption.pyi b/seamless/html/caption.pyi
new file mode 100644
index 0000000..8521418
--- /dev/null
+++ b/seamless/html/caption.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableCaptionElement
+from ..types import ChildType
+
+
+class Caption(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableCaptionElement]): ...
diff --git a/seamless/html/cite.py b/seamless/html/cite.py
deleted file mode 100644
index c47a1ae..0000000
--- a/seamless/html/cite.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Cite(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "cite"
diff --git a/seamless/html/cite.pyi b/seamless/html/cite.pyi
new file mode 100644
index 0000000..825382e
--- /dev/null
+++ b/seamless/html/cite.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Cite(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/code.py b/seamless/html/code.py
deleted file mode 100644
index f13dbee..0000000
--- a/seamless/html/code.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Code(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "code"
diff --git a/seamless/html/code.pyi b/seamless/html/code.pyi
new file mode 100644
index 0000000..4e3f5d2
--- /dev/null
+++ b/seamless/html/code.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Code(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/col.py b/seamless/html/col.py
deleted file mode 100644
index edd3c18..0000000
--- a/seamless/html/col.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableColElement
-    from ..types import ChildType
-
-class Col(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableColElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "col"
-    inline = True
diff --git a/seamless/html/col.pyi b/seamless/html/col.pyi
new file mode 100644
index 0000000..13acd36
--- /dev/null
+++ b/seamless/html/col.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableColElement
+from ..types import ChildType
+
+
+class Col(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableColElement]): ...
diff --git a/seamless/html/colgroup.py b/seamless/html/colgroup.py
deleted file mode 100644
index afd6fc8..0000000
--- a/seamless/html/colgroup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableColElement
-    from ..types import ChildType
-
-class ColGroup(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableColElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "colgroup"
diff --git a/seamless/html/colgroup.pyi b/seamless/html/colgroup.pyi
new file mode 100644
index 0000000..88e5755
--- /dev/null
+++ b/seamless/html/colgroup.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableColElement
+from ..types import ChildType
+
+
+class ColGroup(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableColElement]): ...
diff --git a/seamless/html/data.py b/seamless/html/data.py
deleted file mode 100644
index 3d794f9..0000000
--- a/seamless/html/data.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLDataElement
-    from ..types import ChildType
-
-class Data(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLDataElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "data"
diff --git a/seamless/html/data.pyi b/seamless/html/data.pyi
new file mode 100644
index 0000000..c59a986
--- /dev/null
+++ b/seamless/html/data.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLDataElement
+from ..types import ChildType
+
+
+class Data(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLDataElement]): ...
diff --git a/seamless/html/datalist.py b/seamless/html/datalist.py
deleted file mode 100644
index 97d5e48..0000000
--- a/seamless/html/datalist.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLDataListElement
-    from ..types import ChildType
-
-class DataList(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLDataListElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "datalist"
diff --git a/seamless/html/datalist.pyi b/seamless/html/datalist.pyi
new file mode 100644
index 0000000..414a0bd
--- /dev/null
+++ b/seamless/html/datalist.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLDataListElement
+from ..types import ChildType
+
+
+class DataList(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLDataListElement]): ...
diff --git a/seamless/html/dd.py b/seamless/html/dd.py
deleted file mode 100644
index fedec17..0000000
--- a/seamless/html/dd.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Dd(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "dd"
diff --git a/seamless/html/dd.pyi b/seamless/html/dd.pyi
new file mode 100644
index 0000000..d76da11
--- /dev/null
+++ b/seamless/html/dd.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Dd(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/del_.py b/seamless/html/del_.py
deleted file mode 100644
index 687b4fa..0000000
--- a/seamless/html/del_.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLModElement
-    from ..types import ChildType
-
-class Del(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLModElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "del"
diff --git a/seamless/html/del_.pyi b/seamless/html/del_.pyi
new file mode 100644
index 0000000..b0652eb
--- /dev/null
+++ b/seamless/html/del_.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLModElement
+from ..types import ChildType
+
+
+class Del(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLModElement]): ...
diff --git a/seamless/html/details.py b/seamless/html/details.py
deleted file mode 100644
index 9195758..0000000
--- a/seamless/html/details.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLDetailsElement
-    from ..types import ChildType
-
-class Details(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLDetailsElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "details"
diff --git a/seamless/html/details.pyi b/seamless/html/details.pyi
new file mode 100644
index 0000000..0851faa
--- /dev/null
+++ b/seamless/html/details.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLDetailsElement
+from ..types import ChildType
+
+
+class Details(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLDetailsElement]): ...
diff --git a/seamless/html/dfn.py b/seamless/html/dfn.py
deleted file mode 100644
index 47e5586..0000000
--- a/seamless/html/dfn.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Dfn(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "dfn"
diff --git a/seamless/html/dfn.pyi b/seamless/html/dfn.pyi
new file mode 100644
index 0000000..a7301d4
--- /dev/null
+++ b/seamless/html/dfn.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Dfn(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/dialog.py b/seamless/html/dialog.py
deleted file mode 100644
index ef1e537..0000000
--- a/seamless/html/dialog.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLDialogElement
-    from ..types import ChildType
-
-class Dialog(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLDialogElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "dialog"
diff --git a/seamless/html/dialog.pyi b/seamless/html/dialog.pyi
new file mode 100644
index 0000000..2bd55b6
--- /dev/null
+++ b/seamless/html/dialog.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLDialogElement
+from ..types import ChildType
+
+
+class Dialog(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLDialogElement]): ...
diff --git a/seamless/html/div.py b/seamless/html/div.py
deleted file mode 100644
index d36194a..0000000
--- a/seamless/html/div.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLDivElement
-    from ..types import ChildType
-
-class Div(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLDivElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "div"
diff --git a/seamless/html/div.pyi b/seamless/html/div.pyi
new file mode 100644
index 0000000..cdb7d77
--- /dev/null
+++ b/seamless/html/div.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLDivElement
+from ..types import ChildType
+
+
+class Div(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLDivElement]): ...
diff --git a/seamless/html/dl.py b/seamless/html/dl.py
deleted file mode 100644
index 2306bae..0000000
--- a/seamless/html/dl.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Dl(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "dl"
diff --git a/seamless/html/dl.pyi b/seamless/html/dl.pyi
new file mode 100644
index 0000000..e94c5e9
--- /dev/null
+++ b/seamless/html/dl.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Dl(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/dt.py b/seamless/html/dt.py
deleted file mode 100644
index 75eb725..0000000
--- a/seamless/html/dt.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Dt(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "dt"
diff --git a/seamless/html/dt.pyi b/seamless/html/dt.pyi
new file mode 100644
index 0000000..5468215
--- /dev/null
+++ b/seamless/html/dt.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Dt(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/em.py b/seamless/html/em.py
deleted file mode 100644
index adcfe39..0000000
--- a/seamless/html/em.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Em(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "em"
diff --git a/seamless/html/em.pyi b/seamless/html/em.pyi
new file mode 100644
index 0000000..dbd781d
--- /dev/null
+++ b/seamless/html/em.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Em(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/embed.py b/seamless/html/embed.py
deleted file mode 100644
index e3b7ca4..0000000
--- a/seamless/html/embed.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLEmbedElement
-    from ..types import ChildType
-
-class Embed(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLEmbedElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "embed"
-    inline = True
diff --git a/seamless/html/embed.pyi b/seamless/html/embed.pyi
new file mode 100644
index 0000000..f0f8c8e
--- /dev/null
+++ b/seamless/html/embed.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLEmbedElement
+from ..types import ChildType
+
+
+class Embed(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLEmbedElement]): ...
diff --git a/seamless/html/fieldset.py b/seamless/html/fieldset.py
deleted file mode 100644
index 3cb01c8..0000000
--- a/seamless/html/fieldset.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLFieldSetElement
-    from ..types import ChildType
-
-class FieldSet(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLFieldSetElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "fieldset"
diff --git a/seamless/html/fieldset.pyi b/seamless/html/fieldset.pyi
new file mode 100644
index 0000000..e1b5bb8
--- /dev/null
+++ b/seamless/html/fieldset.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLFieldSetElement
+from ..types import ChildType
+
+
+class FieldSet(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLFieldSetElement]): ...
diff --git a/seamless/html/figcaption.py b/seamless/html/figcaption.py
deleted file mode 100644
index c01772c..0000000
--- a/seamless/html/figcaption.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class FigCaption(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "figcaption"
diff --git a/seamless/html/figcaption.pyi b/seamless/html/figcaption.pyi
new file mode 100644
index 0000000..5f9c886
--- /dev/null
+++ b/seamless/html/figcaption.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class FigCaption(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/figure.py b/seamless/html/figure.py
deleted file mode 100644
index 64c9ffe..0000000
--- a/seamless/html/figure.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Figure(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "figure"
diff --git a/seamless/html/figure.pyi b/seamless/html/figure.pyi
new file mode 100644
index 0000000..63395dd
--- /dev/null
+++ b/seamless/html/figure.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Figure(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/footer.py b/seamless/html/footer.py
deleted file mode 100644
index 7fb90a3..0000000
--- a/seamless/html/footer.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Footer(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "footer"
diff --git a/seamless/html/footer.pyi b/seamless/html/footer.pyi
new file mode 100644
index 0000000..2f1014c
--- /dev/null
+++ b/seamless/html/footer.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Footer(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/form.py b/seamless/html/form.py
deleted file mode 100644
index 6a0c218..0000000
--- a/seamless/html/form.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLFormElement
-    from ..types import ChildType
-
-class Form(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLFormElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "form"
diff --git a/seamless/html/form.pyi b/seamless/html/form.pyi
new file mode 100644
index 0000000..e7882e4
--- /dev/null
+++ b/seamless/html/form.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLFormElement
+from ..types import ChildType
+
+
+class Form(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLFormElement]): ...
diff --git a/seamless/html/fragment.py b/seamless/html/fragment.py
deleted file mode 100644
index 4e4dae5..0000000
--- a/seamless/html/fragment.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from ..element import Element
-
-
-class Fragment(Element):
-    tag_name = ""
diff --git a/seamless/html/fragment.pyi b/seamless/html/fragment.pyi
new file mode 100644
index 0000000..ac4fb11
--- /dev/null
+++ b/seamless/html/fragment.pyi
@@ -0,0 +1,3 @@
+from pydom.element import Element
+
+class Fragment(Element): ...
diff --git a/seamless/html/h1.py b/seamless/html/h1.py
deleted file mode 100644
index ebe1efb..0000000
--- a/seamless/html/h1.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H1(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h1"
diff --git a/seamless/html/h1.pyi b/seamless/html/h1.pyi
new file mode 100644
index 0000000..a1771bc
--- /dev/null
+++ b/seamless/html/h1.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H1(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/h2.py b/seamless/html/h2.py
deleted file mode 100644
index b90f824..0000000
--- a/seamless/html/h2.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H2(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h2"
diff --git a/seamless/html/h2.pyi b/seamless/html/h2.pyi
new file mode 100644
index 0000000..a2d7772
--- /dev/null
+++ b/seamless/html/h2.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H2(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/h3.py b/seamless/html/h3.py
deleted file mode 100644
index 1a035aa..0000000
--- a/seamless/html/h3.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H3(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h3"
diff --git a/seamless/html/h3.pyi b/seamless/html/h3.pyi
new file mode 100644
index 0000000..402bab5
--- /dev/null
+++ b/seamless/html/h3.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H3(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/h4.py b/seamless/html/h4.py
deleted file mode 100644
index 5440f50..0000000
--- a/seamless/html/h4.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H4(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h4"
diff --git a/seamless/html/h4.pyi b/seamless/html/h4.pyi
new file mode 100644
index 0000000..c09cb2f
--- /dev/null
+++ b/seamless/html/h4.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H4(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/h5.py b/seamless/html/h5.py
deleted file mode 100644
index e181e91..0000000
--- a/seamless/html/h5.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H5(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h5"
diff --git a/seamless/html/h5.pyi b/seamless/html/h5.pyi
new file mode 100644
index 0000000..bcdc3ad
--- /dev/null
+++ b/seamless/html/h5.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H5(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/h6.py b/seamless/html/h6.py
deleted file mode 100644
index 65db91d..0000000
--- a/seamless/html/h6.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadingElement
-    from ..types import ChildType
-
-class H6(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadingElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "h6"
diff --git a/seamless/html/h6.pyi b/seamless/html/h6.pyi
new file mode 100644
index 0000000..56ce191
--- /dev/null
+++ b/seamless/html/h6.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadingElement
+from ..types import ChildType
+
+
+class H6(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadingElement]): ...
diff --git a/seamless/html/head.py b/seamless/html/head.py
deleted file mode 100644
index a0bdcbf..0000000
--- a/seamless/html/head.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHeadElement
-    from ..types import ChildType
-
-class Head(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHeadElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "head"
diff --git a/seamless/html/head.pyi b/seamless/html/head.pyi
new file mode 100644
index 0000000..2710933
--- /dev/null
+++ b/seamless/html/head.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHeadElement
+from ..types import ChildType
+
+
+class Head(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHeadElement]): ...
diff --git a/seamless/html/header.py b/seamless/html/header.py
deleted file mode 100644
index cef02a7..0000000
--- a/seamless/html/header.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Header(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "header"
diff --git a/seamless/html/header.pyi b/seamless/html/header.pyi
new file mode 100644
index 0000000..1f5f884
--- /dev/null
+++ b/seamless/html/header.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Header(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/hgroup.py b/seamless/html/hgroup.py
deleted file mode 100644
index 394e178..0000000
--- a/seamless/html/hgroup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class HGroup(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "hgroup"
diff --git a/seamless/html/hgroup.pyi b/seamless/html/hgroup.pyi
new file mode 100644
index 0000000..3ea7ea7
--- /dev/null
+++ b/seamless/html/hgroup.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class HGroup(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/hr.py b/seamless/html/hr.py
deleted file mode 100644
index c49f044..0000000
--- a/seamless/html/hr.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHRElement
-    from ..types import ChildType
-
-class Hr(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHRElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "hr"
-    inline = True
diff --git a/seamless/html/hr.pyi b/seamless/html/hr.pyi
new file mode 100644
index 0000000..86b56bf
--- /dev/null
+++ b/seamless/html/hr.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHRElement
+from ..types import ChildType
+
+
+class Hr(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHRElement]): ...
diff --git a/seamless/html/html.py b/seamless/html/html.py
deleted file mode 100644
index e44754d..0000000
--- a/seamless/html/html.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLHtmlElement
-    from ..types import ChildType
-
-class Html(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLHtmlElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "html"
diff --git a/seamless/html/html.pyi b/seamless/html/html.pyi
new file mode 100644
index 0000000..3e4f163
--- /dev/null
+++ b/seamless/html/html.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLHtmlElement
+from ..types import ChildType
+
+
+class Html(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLHtmlElement]): ...
diff --git a/seamless/html/i.py b/seamless/html/i.py
deleted file mode 100644
index 4262ec6..0000000
--- a/seamless/html/i.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class I(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "i"
diff --git a/seamless/html/i.pyi b/seamless/html/i.pyi
new file mode 100644
index 0000000..9418b89
--- /dev/null
+++ b/seamless/html/i.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class I(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/iframe.py b/seamless/html/iframe.py
deleted file mode 100644
index a72c5e5..0000000
--- a/seamless/html/iframe.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLIFrameElement
-    from ..types import ChildType
-
-class IFrame(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLIFrameElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "iframe"
diff --git a/seamless/html/iframe.pyi b/seamless/html/iframe.pyi
new file mode 100644
index 0000000..3cdd5e3
--- /dev/null
+++ b/seamless/html/iframe.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLIFrameElement
+from ..types import ChildType
+
+
+class IFrame(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLIFrameElement]): ...
diff --git a/seamless/html/img.py b/seamless/html/img.py
deleted file mode 100644
index 25672b4..0000000
--- a/seamless/html/img.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLImageElement
-    from ..types import ChildType
-
-class Img(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLImageElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "img"
-    inline = True
diff --git a/seamless/html/img.pyi b/seamless/html/img.pyi
new file mode 100644
index 0000000..f1bbb62
--- /dev/null
+++ b/seamless/html/img.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLImageElement
+from ..types import ChildType
+
+
+class Img(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLImageElement]): ...
diff --git a/seamless/html/input.py b/seamless/html/input.py
deleted file mode 100644
index fd11df6..0000000
--- a/seamless/html/input.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLInputElement
-    from ..types import ChildType
-
-class Input(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLInputElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "input"
-    inline = True
diff --git a/seamless/html/input.pyi b/seamless/html/input.pyi
new file mode 100644
index 0000000..b73eec6
--- /dev/null
+++ b/seamless/html/input.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLInputElement
+from ..types import ChildType
+
+
+class Input(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLInputElement]): ...
diff --git a/seamless/html/ins.py b/seamless/html/ins.py
deleted file mode 100644
index 1f46a72..0000000
--- a/seamless/html/ins.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLModElement
-    from ..types import ChildType
-
-class Ins(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLModElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "ins"
diff --git a/seamless/html/ins.pyi b/seamless/html/ins.pyi
new file mode 100644
index 0000000..b186d8c
--- /dev/null
+++ b/seamless/html/ins.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLModElement
+from ..types import ChildType
+
+
+class Ins(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLModElement]): ...
diff --git a/seamless/html/kbd.py b/seamless/html/kbd.py
deleted file mode 100644
index 74fdf4c..0000000
--- a/seamless/html/kbd.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Kbd(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "kbd"
diff --git a/seamless/html/kbd.pyi b/seamless/html/kbd.pyi
new file mode 100644
index 0000000..4a64cc0
--- /dev/null
+++ b/seamless/html/kbd.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Kbd(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/label.py b/seamless/html/label.py
deleted file mode 100644
index 49b6f55..0000000
--- a/seamless/html/label.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLLabelElement
-    from ..types import ChildType
-
-class Label(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLLabelElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "label"
diff --git a/seamless/html/label.pyi b/seamless/html/label.pyi
new file mode 100644
index 0000000..c0dcfe6
--- /dev/null
+++ b/seamless/html/label.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLLabelElement
+from ..types import ChildType
+
+
+class Label(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLLabelElement]): ...
diff --git a/seamless/html/legend.py b/seamless/html/legend.py
deleted file mode 100644
index 6c57fb2..0000000
--- a/seamless/html/legend.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLLegendElement
-    from ..types import ChildType
-
-class Legend(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLLegendElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "legend"
diff --git a/seamless/html/legend.pyi b/seamless/html/legend.pyi
new file mode 100644
index 0000000..3c7f9e1
--- /dev/null
+++ b/seamless/html/legend.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLLegendElement
+from ..types import ChildType
+
+
+class Legend(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLLegendElement]): ...
diff --git a/seamless/html/li.py b/seamless/html/li.py
deleted file mode 100644
index f07895d..0000000
--- a/seamless/html/li.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLListItemElement
-    from ..types import ChildType
-
-class Li(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLListItemElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "li"
diff --git a/seamless/html/li.pyi b/seamless/html/li.pyi
new file mode 100644
index 0000000..1b6f85b
--- /dev/null
+++ b/seamless/html/li.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLListItemElement
+from ..types import ChildType
+
+
+class Li(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLListItemElement]): ...
diff --git a/seamless/html/link.py b/seamless/html/link.py
deleted file mode 100644
index 9c02d3d..0000000
--- a/seamless/html/link.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLLinkElement
-    from ..types import ChildType
-
-class Link(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLLinkElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "link"
-    inline = True
diff --git a/seamless/html/link.pyi b/seamless/html/link.pyi
new file mode 100644
index 0000000..2b4bafd
--- /dev/null
+++ b/seamless/html/link.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLLinkElement
+from ..types import ChildType
+
+
+class Link(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLLinkElement]): ...
diff --git a/seamless/html/main.py b/seamless/html/main.py
deleted file mode 100644
index 4d9210d..0000000
--- a/seamless/html/main.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Main(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "main"
diff --git a/seamless/html/main.pyi b/seamless/html/main.pyi
new file mode 100644
index 0000000..57a2f4a
--- /dev/null
+++ b/seamless/html/main.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Main(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/map.py b/seamless/html/map.py
deleted file mode 100644
index 0ab99e6..0000000
--- a/seamless/html/map.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLMapElement
-    from ..types import ChildType
-
-class Map(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLMapElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "map"
diff --git a/seamless/html/map.pyi b/seamless/html/map.pyi
new file mode 100644
index 0000000..27fe304
--- /dev/null
+++ b/seamless/html/map.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLMapElement
+from ..types import ChildType
+
+
+class Map(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLMapElement]): ...
diff --git a/seamless/html/mark.py b/seamless/html/mark.py
deleted file mode 100644
index 733099f..0000000
--- a/seamless/html/mark.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Mark(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "mark"
diff --git a/seamless/html/mark.pyi b/seamless/html/mark.pyi
new file mode 100644
index 0000000..056867d
--- /dev/null
+++ b/seamless/html/mark.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Mark(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/menu.py b/seamless/html/menu.py
deleted file mode 100644
index ab28380..0000000
--- a/seamless/html/menu.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Menu(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "menu"
diff --git a/seamless/html/menu.pyi b/seamless/html/menu.pyi
new file mode 100644
index 0000000..bb399b3
--- /dev/null
+++ b/seamless/html/menu.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Menu(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/meta.py b/seamless/html/meta.py
deleted file mode 100644
index 64ef664..0000000
--- a/seamless/html/meta.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Literal, overload
-from ..types import ChildType
-from ..element import Element
-
-
-HttpEquivOptions = Literal["content-security-policy", "content-type", "default-style", "x-ua-compatible", "refresh"]
-
-
-class Meta(Element):
-
-    @overload
-    def __init__(self, *children: ChildType, http_equiv: HttpEquivOptions, content: str): ...
-    @overload
-    def __init__(self, *children: ChildType, name: str, content: str): ...
-    @overload
-    def __init__(self, *children: ChildType, charset: str): ...
-    @overload
-    def __init__(self, *children: ChildType, itemprop: str): ...
-
-    def __init__(self, *children: ChildType, **kwargs):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "meta"
-    inline = True
diff --git a/seamless/html/meta.pyi b/seamless/html/meta.pyi
new file mode 100644
index 0000000..246f21c
--- /dev/null
+++ b/seamless/html/meta.pyi
@@ -0,0 +1,28 @@
+from typing import Literal, overload
+from pydom.element import Element
+
+from ..types import ChildType
+
+HttpEquivOptions = Literal[
+    "content-security-policy",
+    "content-type",
+    "default-style",
+    "x-ua-compatible",
+    "refresh",
+]
+
+
+class Meta(Element):
+
+    @overload
+    def __init__(
+        self, *children: ChildType, http_equiv: HttpEquivOptions, content: str
+    ): ...
+    @overload
+    def __init__(self, *children: ChildType, name: str, content: str): ...
+    @overload
+    def __init__(self, *children: ChildType, charset: str): ...
+    @overload
+    def __init__(self, *children: ChildType, itemprop: str): ...
+
+    def __init__(self, *children: ChildType, **kwargs): ...
diff --git a/seamless/html/meter.py b/seamless/html/meter.py
deleted file mode 100644
index a02b853..0000000
--- a/seamless/html/meter.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLMeterElement
-    from ..types import ChildType
-
-class Meter(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLMeterElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "meter"
diff --git a/seamless/html/meter.pyi b/seamless/html/meter.pyi
new file mode 100644
index 0000000..461c3a3
--- /dev/null
+++ b/seamless/html/meter.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLMeterElement
+from ..types import ChildType
+
+
+class Meter(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLMeterElement]): ...
diff --git a/seamless/html/nav.py b/seamless/html/nav.py
deleted file mode 100644
index ca5a4fa..0000000
--- a/seamless/html/nav.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Nav(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "nav"
diff --git a/seamless/html/nav.pyi b/seamless/html/nav.pyi
new file mode 100644
index 0000000..ac13956
--- /dev/null
+++ b/seamless/html/nav.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Nav(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/noscript.py b/seamless/html/noscript.py
deleted file mode 100644
index 082a1ac..0000000
--- a/seamless/html/noscript.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class NoScript(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "noscript"
diff --git a/seamless/html/noscript.pyi b/seamless/html/noscript.pyi
new file mode 100644
index 0000000..e7eb994
--- /dev/null
+++ b/seamless/html/noscript.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class NoScript(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/object.py b/seamless/html/object.py
deleted file mode 100644
index c75f1e5..0000000
--- a/seamless/html/object.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLObjectElement
-    from ..types import ChildType
-
-class Object(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLObjectElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "object"
diff --git a/seamless/html/object.pyi b/seamless/html/object.pyi
new file mode 100644
index 0000000..14f0fa7
--- /dev/null
+++ b/seamless/html/object.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLObjectElement
+from ..types import ChildType
+
+
+class Object(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLObjectElement]): ...
diff --git a/seamless/html/ol.py b/seamless/html/ol.py
deleted file mode 100644
index af89239..0000000
--- a/seamless/html/ol.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLOrderedListElement
-    from ..types import ChildType
-
-class Ol(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLOrderedListElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "ol"
diff --git a/seamless/html/ol.pyi b/seamless/html/ol.pyi
new file mode 100644
index 0000000..aedba81
--- /dev/null
+++ b/seamless/html/ol.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLOrderedListElement
+from ..types import ChildType
+
+
+class Ol(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLOrderedListElement]): ...
diff --git a/seamless/html/optgroup.py b/seamless/html/optgroup.py
deleted file mode 100644
index 46f1f91..0000000
--- a/seamless/html/optgroup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLOptGroupElement
-    from ..types import ChildType
-
-class OptGroup(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLOptGroupElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "optgroup"
diff --git a/seamless/html/optgroup.pyi b/seamless/html/optgroup.pyi
new file mode 100644
index 0000000..5c6c8ae
--- /dev/null
+++ b/seamless/html/optgroup.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLOptGroupElement
+from ..types import ChildType
+
+
+class OptGroup(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLOptGroupElement]): ...
diff --git a/seamless/html/option.py b/seamless/html/option.py
deleted file mode 100644
index f6f7f5c..0000000
--- a/seamless/html/option.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLOptionElement
-    from ..types import ChildType
-
-class Option(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLOptionElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "option"
diff --git a/seamless/html/option.pyi b/seamless/html/option.pyi
new file mode 100644
index 0000000..ebe4cfd
--- /dev/null
+++ b/seamless/html/option.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLOptionElement
+from ..types import ChildType
+
+
+class Option(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLOptionElement]): ...
diff --git a/seamless/html/output.py b/seamless/html/output.py
deleted file mode 100644
index cce2810..0000000
--- a/seamless/html/output.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLOutputElement
-    from ..types import ChildType
-
-class Output(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLOutputElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "output"
diff --git a/seamless/html/output.pyi b/seamless/html/output.pyi
new file mode 100644
index 0000000..23bb075
--- /dev/null
+++ b/seamless/html/output.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLOutputElement
+from ..types import ChildType
+
+
+class Output(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLOutputElement]): ...
diff --git a/seamless/html/p.py b/seamless/html/p.py
deleted file mode 100644
index ebbe2c5..0000000
--- a/seamless/html/p.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLParagraphElement
-    from ..types import ChildType
-
-class P(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLParagraphElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "p"
diff --git a/seamless/html/p.pyi b/seamless/html/p.pyi
new file mode 100644
index 0000000..d31ba3d
--- /dev/null
+++ b/seamless/html/p.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLParagraphElement
+from ..types import ChildType
+
+
+class P(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLParagraphElement]): ...
diff --git a/seamless/html/param.py b/seamless/html/param.py
deleted file mode 100644
index 15f45c9..0000000
--- a/seamless/html/param.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLParamElement
-    from ..types import ChildType
-
-class Param(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLParamElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "param"
-    inline = True
diff --git a/seamless/html/param.pyi b/seamless/html/param.pyi
new file mode 100644
index 0000000..2c4067f
--- /dev/null
+++ b/seamless/html/param.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLParamElement
+from ..types import ChildType
+
+
+class Param(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLParamElement]): ...
diff --git a/seamless/html/picture.py b/seamless/html/picture.py
deleted file mode 100644
index c43f9ac..0000000
--- a/seamless/html/picture.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLPictureElement
-    from ..types import ChildType
-
-class Picture(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLPictureElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "picture"
diff --git a/seamless/html/picture.pyi b/seamless/html/picture.pyi
new file mode 100644
index 0000000..017dd08
--- /dev/null
+++ b/seamless/html/picture.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLPictureElement
+from ..types import ChildType
+
+
+class Picture(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLPictureElement]): ...
diff --git a/seamless/html/pre.py b/seamless/html/pre.py
deleted file mode 100644
index 2fec69d..0000000
--- a/seamless/html/pre.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLPreElement
-    from ..types import ChildType
-
-class Pre(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLPreElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "pre"
diff --git a/seamless/html/pre.pyi b/seamless/html/pre.pyi
new file mode 100644
index 0000000..74c4882
--- /dev/null
+++ b/seamless/html/pre.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLPreElement
+from ..types import ChildType
+
+
+class Pre(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLPreElement]): ...
diff --git a/seamless/html/progress.py b/seamless/html/progress.py
deleted file mode 100644
index c7a4595..0000000
--- a/seamless/html/progress.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLProgressElement
-    from ..types import ChildType
-
-class Progress(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLProgressElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "progress"
diff --git a/seamless/html/progress.pyi b/seamless/html/progress.pyi
new file mode 100644
index 0000000..a40007a
--- /dev/null
+++ b/seamless/html/progress.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLProgressElement
+from ..types import ChildType
+
+
+class Progress(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLProgressElement]): ...
diff --git a/seamless/html/q.py b/seamless/html/q.py
deleted file mode 100644
index a7c721e..0000000
--- a/seamless/html/q.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLQuoteElement
-    from ..types import ChildType
-
-class Q(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLQuoteElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "q"
diff --git a/seamless/html/q.pyi b/seamless/html/q.pyi
new file mode 100644
index 0000000..e8a2935
--- /dev/null
+++ b/seamless/html/q.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLQuoteElement
+from ..types import ChildType
+
+
+class Q(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLQuoteElement]): ...
diff --git a/seamless/html/rp.py b/seamless/html/rp.py
deleted file mode 100644
index 50716a4..0000000
--- a/seamless/html/rp.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Rp(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "rp"
diff --git a/seamless/html/rp.pyi b/seamless/html/rp.pyi
new file mode 100644
index 0000000..a272916
--- /dev/null
+++ b/seamless/html/rp.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Rp(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/rt.py b/seamless/html/rt.py
deleted file mode 100644
index 72786e5..0000000
--- a/seamless/html/rt.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Rt(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "rt"
diff --git a/seamless/html/rt.pyi b/seamless/html/rt.pyi
new file mode 100644
index 0000000..72693b0
--- /dev/null
+++ b/seamless/html/rt.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Rt(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/ruby.py b/seamless/html/ruby.py
deleted file mode 100644
index 222209e..0000000
--- a/seamless/html/ruby.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Ruby(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "ruby"
diff --git a/seamless/html/ruby.pyi b/seamless/html/ruby.pyi
new file mode 100644
index 0000000..ff53752
--- /dev/null
+++ b/seamless/html/ruby.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Ruby(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/s.py b/seamless/html/s.py
deleted file mode 100644
index 1e07572..0000000
--- a/seamless/html/s.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class S(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "s"
diff --git a/seamless/html/s.pyi b/seamless/html/s.pyi
new file mode 100644
index 0000000..f5e7393
--- /dev/null
+++ b/seamless/html/s.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class S(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/samp.py b/seamless/html/samp.py
deleted file mode 100644
index a3ee45f..0000000
--- a/seamless/html/samp.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Samp(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "samp"
diff --git a/seamless/html/samp.pyi b/seamless/html/samp.pyi
new file mode 100644
index 0000000..d3214d2
--- /dev/null
+++ b/seamless/html/samp.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Samp(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/script.py b/seamless/html/script.py
deleted file mode 100644
index 8a49010..0000000
--- a/seamless/html/script.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLScriptElement
-    from ..types import ChildType
-
-class Script(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLScriptElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "script"
diff --git a/seamless/html/script.pyi b/seamless/html/script.pyi
new file mode 100644
index 0000000..019d5a0
--- /dev/null
+++ b/seamless/html/script.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLScriptElement
+from ..types import ChildType
+
+
+class Script(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLScriptElement]): ...
diff --git a/seamless/html/search.py b/seamless/html/search.py
deleted file mode 100644
index f4fd98b..0000000
--- a/seamless/html/search.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Search(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "search"
diff --git a/seamless/html/search.pyi b/seamless/html/search.pyi
new file mode 100644
index 0000000..dcc45f8
--- /dev/null
+++ b/seamless/html/search.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Search(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/section.py b/seamless/html/section.py
deleted file mode 100644
index 59f481a..0000000
--- a/seamless/html/section.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Section(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "section"
diff --git a/seamless/html/section.pyi b/seamless/html/section.pyi
new file mode 100644
index 0000000..188559c
--- /dev/null
+++ b/seamless/html/section.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Section(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/select.py b/seamless/html/select.py
deleted file mode 100644
index cff155e..0000000
--- a/seamless/html/select.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLSelectElement
-    from ..types import ChildType
-
-class Select(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLSelectElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "select"
diff --git a/seamless/html/select.pyi b/seamless/html/select.pyi
new file mode 100644
index 0000000..3710443
--- /dev/null
+++ b/seamless/html/select.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLSelectElement
+from ..types import ChildType
+
+
+class Select(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLSelectElement]): ...
diff --git a/seamless/html/slot.py b/seamless/html/slot.py
deleted file mode 100644
index 5d406b8..0000000
--- a/seamless/html/slot.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLSlotElement
-    from ..types import ChildType
-
-class Slot(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLSlotElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "slot"
diff --git a/seamless/html/slot.pyi b/seamless/html/slot.pyi
new file mode 100644
index 0000000..0c2e55e
--- /dev/null
+++ b/seamless/html/slot.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLSlotElement
+from ..types import ChildType
+
+
+class Slot(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLSlotElement]): ...
diff --git a/seamless/html/small.py b/seamless/html/small.py
deleted file mode 100644
index 80ae1ca..0000000
--- a/seamless/html/small.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Small(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "small"
diff --git a/seamless/html/small.pyi b/seamless/html/small.pyi
new file mode 100644
index 0000000..5dd88ef
--- /dev/null
+++ b/seamless/html/small.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Small(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/source.py b/seamless/html/source.py
deleted file mode 100644
index 283f44e..0000000
--- a/seamless/html/source.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLSourceElement
-    from ..types import ChildType
-
-class Source(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLSourceElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "source"
-    inline = True
diff --git a/seamless/html/source.pyi b/seamless/html/source.pyi
new file mode 100644
index 0000000..1293d0c
--- /dev/null
+++ b/seamless/html/source.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLSourceElement
+from ..types import ChildType
+
+
+class Source(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLSourceElement]): ...
diff --git a/seamless/html/span.py b/seamless/html/span.py
deleted file mode 100644
index 3113c18..0000000
--- a/seamless/html/span.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLSpanElement
-    from ..types import ChildType
-
-class Span(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLSpanElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "span"
diff --git a/seamless/html/span.pyi b/seamless/html/span.pyi
new file mode 100644
index 0000000..188694b
--- /dev/null
+++ b/seamless/html/span.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLSpanElement
+from ..types import ChildType
+
+
+class Span(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLSpanElement]): ...
diff --git a/seamless/html/strong.py b/seamless/html/strong.py
deleted file mode 100644
index 1e0f02b..0000000
--- a/seamless/html/strong.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Strong(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "strong"
diff --git a/seamless/html/strong.pyi b/seamless/html/strong.pyi
new file mode 100644
index 0000000..1a4239b
--- /dev/null
+++ b/seamless/html/strong.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Strong(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/style.py b/seamless/html/style.py
deleted file mode 100644
index de01fac..0000000
--- a/seamless/html/style.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLStyleElement
-    from ..types import ChildType
-
-class Style(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLStyleElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "style"
diff --git a/seamless/html/style.pyi b/seamless/html/style.pyi
new file mode 100644
index 0000000..88ca5a6
--- /dev/null
+++ b/seamless/html/style.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLStyleElement
+from ..types import ChildType
+
+
+class Style(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLStyleElement]): ...
diff --git a/seamless/html/sub.py b/seamless/html/sub.py
deleted file mode 100644
index d5dd51d..0000000
--- a/seamless/html/sub.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Sub(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "sub"
diff --git a/seamless/html/sub.pyi b/seamless/html/sub.pyi
new file mode 100644
index 0000000..d5ed8fd
--- /dev/null
+++ b/seamless/html/sub.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Sub(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/summary.py b/seamless/html/summary.py
deleted file mode 100644
index f8ce75f..0000000
--- a/seamless/html/summary.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Summary(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "summary"
diff --git a/seamless/html/summary.pyi b/seamless/html/summary.pyi
new file mode 100644
index 0000000..f1272f8
--- /dev/null
+++ b/seamless/html/summary.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Summary(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/sup.py b/seamless/html/sup.py
deleted file mode 100644
index ae8c012..0000000
--- a/seamless/html/sup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Sup(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "sup"
diff --git a/seamless/html/sup.pyi b/seamless/html/sup.pyi
new file mode 100644
index 0000000..fb82231
--- /dev/null
+++ b/seamless/html/sup.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Sup(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/svg.py b/seamless/html/svg.py
deleted file mode 100644
index 5a79329..0000000
--- a/seamless/html/svg.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Svg(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "svg"
diff --git a/seamless/html/svg.pyi b/seamless/html/svg.pyi
new file mode 100644
index 0000000..0b9c1bd
--- /dev/null
+++ b/seamless/html/svg.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Svg(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/table.py b/seamless/html/table.py
deleted file mode 100644
index aee3dfe..0000000
--- a/seamless/html/table.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableElement
-    from ..types import ChildType
-
-class Table(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "table"
diff --git a/seamless/html/table.pyi b/seamless/html/table.pyi
new file mode 100644
index 0000000..ce8d857
--- /dev/null
+++ b/seamless/html/table.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableElement
+from ..types import ChildType
+
+
+class Table(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableElement]): ...
diff --git a/seamless/html/tbody.py b/seamless/html/tbody.py
deleted file mode 100644
index a64ee8e..0000000
--- a/seamless/html/tbody.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableSectionElement
-    from ..types import ChildType
-
-class TBody(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableSectionElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "tbody"
diff --git a/seamless/html/tbody.pyi b/seamless/html/tbody.pyi
new file mode 100644
index 0000000..8b58ec2
--- /dev/null
+++ b/seamless/html/tbody.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableSectionElement
+from ..types import ChildType
+
+
+class TBody(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableSectionElement]): ...
diff --git a/seamless/html/td.py b/seamless/html/td.py
deleted file mode 100644
index 12f853b..0000000
--- a/seamless/html/td.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableDataCellElement
-    from ..types import ChildType
-
-class Td(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableDataCellElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "td"
diff --git a/seamless/html/td.pyi b/seamless/html/td.pyi
new file mode 100644
index 0000000..f431114
--- /dev/null
+++ b/seamless/html/td.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableDataCellElement
+from ..types import ChildType
+
+
+class Td(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableDataCellElement]): ...
diff --git a/seamless/html/template.py b/seamless/html/template.py
deleted file mode 100644
index 98a53bf..0000000
--- a/seamless/html/template.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTemplateElement
-    from ..types import ChildType
-
-class Template(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTemplateElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "template"
diff --git a/seamless/html/template.pyi b/seamless/html/template.pyi
new file mode 100644
index 0000000..f94778e
--- /dev/null
+++ b/seamless/html/template.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTemplateElement
+from ..types import ChildType
+
+
+class Template(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTemplateElement]): ...
diff --git a/seamless/html/textarea.py b/seamless/html/textarea.py
deleted file mode 100644
index b41177f..0000000
--- a/seamless/html/textarea.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTextAreaElement
-    from ..types import ChildType
-
-class TextArea(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTextAreaElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "textarea"
diff --git a/seamless/html/textarea.pyi b/seamless/html/textarea.pyi
new file mode 100644
index 0000000..65f9e2c
--- /dev/null
+++ b/seamless/html/textarea.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTextAreaElement
+from ..types import ChildType
+
+
+class TextArea(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTextAreaElement]): ...
diff --git a/seamless/html/tfoot.py b/seamless/html/tfoot.py
deleted file mode 100644
index e01daad..0000000
--- a/seamless/html/tfoot.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableSectionElement
-    from ..types import ChildType
-
-class TFoot(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableSectionElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "tfoot"
diff --git a/seamless/html/tfoot.pyi b/seamless/html/tfoot.pyi
new file mode 100644
index 0000000..0165c1a
--- /dev/null
+++ b/seamless/html/tfoot.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableSectionElement
+from ..types import ChildType
+
+
+class TFoot(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableSectionElement]): ...
diff --git a/seamless/html/th.py b/seamless/html/th.py
deleted file mode 100644
index 6a82bde..0000000
--- a/seamless/html/th.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableHeaderCellElement
-    from ..types import ChildType
-
-class Th(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableHeaderCellElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "th"
diff --git a/seamless/html/th.pyi b/seamless/html/th.pyi
new file mode 100644
index 0000000..b261bab
--- /dev/null
+++ b/seamless/html/th.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableHeaderCellElement
+from ..types import ChildType
+
+
+class Th(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableHeaderCellElement]): ...
diff --git a/seamless/html/thead.py b/seamless/html/thead.py
deleted file mode 100644
index c39e8ca..0000000
--- a/seamless/html/thead.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableSectionElement
-    from ..types import ChildType
-
-class THead(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableSectionElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "thead"
diff --git a/seamless/html/thead.pyi b/seamless/html/thead.pyi
new file mode 100644
index 0000000..2ecf6b3
--- /dev/null
+++ b/seamless/html/thead.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableSectionElement
+from ..types import ChildType
+
+
+class THead(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableSectionElement]): ...
diff --git a/seamless/html/time.py b/seamless/html/time.py
deleted file mode 100644
index 56945f3..0000000
--- a/seamless/html/time.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTimeElement
-    from ..types import ChildType
-
-class Time(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTimeElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "time"
diff --git a/seamless/html/time.pyi b/seamless/html/time.pyi
new file mode 100644
index 0000000..8772c5d
--- /dev/null
+++ b/seamless/html/time.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTimeElement
+from ..types import ChildType
+
+
+class Time(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTimeElement]): ...
diff --git a/seamless/html/title.py b/seamless/html/title.py
deleted file mode 100644
index 512eaa0..0000000
--- a/seamless/html/title.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTitleElement
-    from ..types import ChildType
-
-class Title(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTitleElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "title"
diff --git a/seamless/html/title.pyi b/seamless/html/title.pyi
new file mode 100644
index 0000000..ccc12ee
--- /dev/null
+++ b/seamless/html/title.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTitleElement
+from ..types import ChildType
+
+
+class Title(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTitleElement]): ...
diff --git a/seamless/html/tr.py b/seamless/html/tr.py
deleted file mode 100644
index 282f33a..0000000
--- a/seamless/html/tr.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTableRowElement
-    from ..types import ChildType
-
-class Tr(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTableRowElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "tr"
diff --git a/seamless/html/tr.pyi b/seamless/html/tr.pyi
new file mode 100644
index 0000000..7e01acd
--- /dev/null
+++ b/seamless/html/tr.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTableRowElement
+from ..types import ChildType
+
+
+class Tr(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTableRowElement]): ...
diff --git a/seamless/html/track.py b/seamless/html/track.py
deleted file mode 100644
index 0b66b6b..0000000
--- a/seamless/html/track.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLTrackElement
-    from ..types import ChildType
-
-class Track(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLTrackElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "track"
-    inline = True
diff --git a/seamless/html/track.pyi b/seamless/html/track.pyi
new file mode 100644
index 0000000..c2fd36b
--- /dev/null
+++ b/seamless/html/track.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLTrackElement
+from ..types import ChildType
+
+
+class Track(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLTrackElement]): ...
diff --git a/seamless/html/u.py b/seamless/html/u.py
deleted file mode 100644
index e4c7a65..0000000
--- a/seamless/html/u.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class U(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "u"
diff --git a/seamless/html/u.pyi b/seamless/html/u.pyi
new file mode 100644
index 0000000..906671c
--- /dev/null
+++ b/seamless/html/u.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class U(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/ul.py b/seamless/html/ul.py
deleted file mode 100644
index 21002c5..0000000
--- a/seamless/html/ul.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLUnorderedListElement
-    from ..types import ChildType
-
-class Ul(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLUnorderedListElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "ul"
diff --git a/seamless/html/ul.pyi b/seamless/html/ul.pyi
new file mode 100644
index 0000000..5e55229
--- /dev/null
+++ b/seamless/html/ul.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLUnorderedListElement
+from ..types import ChildType
+
+
+class Ul(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLUnorderedListElement]): ...
diff --git a/seamless/html/var.py b/seamless/html/var.py
deleted file mode 100644
index b7b65e5..0000000
--- a/seamless/html/var.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Var(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "var"
diff --git a/seamless/html/var.pyi b/seamless/html/var.pyi
new file mode 100644
index 0000000..6fb93d1
--- /dev/null
+++ b/seamless/html/var.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Var(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/html/video.py b/seamless/html/video.py
deleted file mode 100644
index cd4c2df..0000000
--- a/seamless/html/video.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLVideoElement
-    from ..types import ChildType
-
-class Video(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLVideoElement"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "video"
diff --git a/seamless/html/video.pyi b/seamless/html/video.pyi
new file mode 100644
index 0000000..ead65da
--- /dev/null
+++ b/seamless/html/video.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLVideoElement
+from ..types import ChildType
+
+
+class Video(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLVideoElement]): ...
diff --git a/seamless/html/wbr.py b/seamless/html/wbr.py
deleted file mode 100644
index 2567165..0000000
--- a/seamless/html/wbr.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import TYPE_CHECKING, Unpack
-from ..element import Element
-
-if TYPE_CHECKING:
-    from ..types.html import HTMLElementProps
-    from ..types import ChildType
-
-class Wbr(Element):
-    def __init__(self, *children: "ChildType", **kwargs: Unpack["HTMLElementProps"]):
-        super().__init__(*children, **kwargs)
-
-    tag_name = "wbr"
-    inline = True
diff --git a/seamless/html/wbr.pyi b/seamless/html/wbr.pyi
new file mode 100644
index 0000000..8cf7de5
--- /dev/null
+++ b/seamless/html/wbr.pyi
@@ -0,0 +1,9 @@
+from typing_extensions import Unpack
+from pydom.element import Element
+
+from ..types.html import HTMLElementProps
+from ..types import ChildType
+
+
+class Wbr(Element):
+    def __init__(self, *children: ChildType, **kwargs: Unpack[HTMLElementProps]): ...
diff --git a/seamless/internal/constants.py b/seamless/internal/constants.py
index 4bd75a6..ecea36b 100644
--- a/seamless/internal/constants.py
+++ b/seamless/internal/constants.py
@@ -1,4 +1,5 @@
 SEAMLESS_ELEMENT_ATTRIBUTE = "seamless:element"
 SEAMLESS_INIT_ATTRIBUTE = "seamless:init"
+SEAMLESS_INIT_ASYNC_ATTRIBUTE = "seamless:async"
 
-CLAIM_COOKIE_NAME = "_seamless_claim_id"
\ No newline at end of file
+DISABLE_GLOBAL_CONTEXT_ENV = "SEAMLESS_DISABLE_GLOBAL_CONTEXT"
\ No newline at end of file
diff --git a/seamless/internal/injector.py b/seamless/internal/injector.py
index 9e88cf3..197cbfa 100644
--- a/seamless/internal/injector.py
+++ b/seamless/internal/injector.py
@@ -1,33 +1,16 @@
-from inspect import iscoroutinefunction, signature
-from typing import Type, TypeVar, overload, Callable, TypeAlias
+from inspect import iscoroutinefunction
+from functools import wraps
+from typing import TypeVar, Callable, TypeAlias
+
+from pydom.utils.injector import Injector as _Injector
 
-from ..internal.utils import wraps
 
 T = TypeVar("T")
 
 InjectFactory: TypeAlias = Callable[[], T]
 
 
-class Injector:
-    """
-    A simple dependency injection container
-
-    To get an instance of a class, you can use the `injector` property of the `Context` class.
-    """
-
-    def __init__(self):
-        self.dependencies = dict[type, Callable]()
-
-    @overload
-    def add(self, cls: Type[T], factory: Callable[[], T], /) -> None: ...
-    @overload
-    def add(self, cls: Type[T], instance: T, /) -> None: ...
-
-    def add(self, cls: Type[T], dependency: Callable[[], T] | T, /) -> None:
-        self.dependencies[cls] = (
-            dependency if callable(dependency) else lambda: dependency
-        )
-
+class Injector(_Injector):
     def inject(self, callback: Callable) -> Callable:
         if iscoroutinefunction(callback):
 
@@ -36,35 +19,6 @@ async def wrapper(*args, **kwargs):  # type: ignore
                 keyword_args = self.inject_params(callback)
                 return await callback(*args, **keyword_args, **kwargs)
 
-        else:
-
-            @wraps(callback)
-            def wrapper(*args, **kwargs):
-                keyword_args = self.inject_params(callback)
-                return callback(*args, **keyword_args, **kwargs)
-
-        return wrapper
-
-    def inject_params(self, callback: Callable):
-        signature_ = signature(callback)
-        parameters = signature_.parameters
-
-        keyword_args = {}
-
-        for name, parameter in parameters.items():
-            if parameter.kind == parameter.POSITIONAL_OR_KEYWORD:
-                if parameter.annotation in self.dependencies:
-                    keyword_args[name] = self.dependencies[parameter.annotation]()
-            elif parameter.kind == parameter.KEYWORD_ONLY:
-                if parameter.annotation in self.dependencies:
-                    keyword_args[name] = self.dependencies[parameter.annotation]()
-
-        return keyword_args
-
-
-def injectable(factory: InjectFactory | None = None):
-    def decorator(cls: Type[T]) -> Type[T]:
-        Injector().add(cls, factory or cls())
-        return cls
+            return wrapper
 
-    return decorator
+        return super().inject(callback)
diff --git a/seamless/internal/singleton.py b/seamless/internal/singleton.py
deleted file mode 100644
index d9544bc..0000000
--- a/seamless/internal/singleton.py
+++ /dev/null
@@ -1,11 +0,0 @@
-class SingletonMeta(type):
-    _instances = {}
-
-    def __call__(cls, *args, **kw):
-        if cls not in cls._instances:
-            cls._instances[cls] = super().__call__(*args, **kw)
-        return cls._instances[cls]
-
-
-class Singleton(metaclass=SingletonMeta):
-    pass
\ No newline at end of file
diff --git a/seamless/internal/utils.py b/seamless/internal/utils.py
index d838524..c798018 100644
--- a/seamless/internal/utils.py
+++ b/seamless/internal/utils.py
@@ -1,12 +1,5 @@
-# type: ignore
-
-from functools import wraps as _wraps
-from inspect import iscoroutinefunction, ismethod
-from string import ascii_letters
-from typing import TypeGuard
-from uuid import uuid4
-
-ascii_length = len(ascii_letters)
+from functools import wraps
+from inspect import iscoroutinefunction, isfunction
 
 
 class Promise:
@@ -35,26 +28,6 @@ def __len__(self):
         return dict.__len__(self) // 2
 
 
-def short_uuid(length=12):
-    original_uuid = uuid4()
-    hex_string = original_uuid.hex
-    base62_uuid = ""
-    for i in range(0, len(hex_string), 2):
-        hex_byte = int(hex_string[i : i + 2], 16)
-        base62_uuid += ascii_letters[hex_byte % ascii_length]
-
-    short_uuid = base62_uuid[:length]
-
-    return short_uuid
-
-
-def to_iter(value):
-    try:
-        return iter(value)
-    except TypeError:
-        return iter((value,))
-
-
 def to_async(func):
     if iscoroutinefunction(func):
         return func
@@ -69,7 +42,7 @@ async def wrapper(*args, **kwargs):
 class _obj(object):
     def __init__(self, d: dict):
         for key, value in d.items():
-            if isinstance(key, (list, tuple)):
+            if isinstance(value, (list, tuple)):
                 setattr(
                     self, key, [_obj(x) if isinstance(x, dict) else x for x in value]
                 )
@@ -77,38 +50,12 @@ def __init__(self, d: dict):
                 setattr(self, key, _obj(value) if isinstance(value, dict) else value)
 
 
-def wraps(info):
-    def inner(callback):
-        if ismethod(info):
-
-            if iscoroutinefunction(callback):
-
-                @_wraps(info.__func__)
-                async def wrapper(_, *args, **kwargs):
-                    return await callback(*args, **kwargs)
-
-            else:
-
-                @_wraps(info.__func__)
-                def wrapper(_, *args, **kwargs):
-                    return callback(*args, **kwargs)
-
-            return wrapper.__get__(info.__self__, info.__class__)
-
-        if iscoroutinefunction(callback):
-
-            @_wraps(info)
-            async def wrapper(*args, **kwargs):
-                return await callback(*args, **kwargs)
-
-        @_wraps(info)
-        def wrapper(*args, **kwargs):
-            return callback(*args, **kwargs)
-        
-        return wrapper
-
-    return inner
+def is_global(func):
+    func = original_func(func)
+    return isfunction(func) and (getattr(func, "__closure__", None) is None)
 
 
-def is_primitive(value) -> TypeGuard[str | int | float | bool | None]:
-    return isinstance(value, (str, int, float, bool)) or value is None
+def original_func(func):
+    if hasattr(func, "__wrapped__"):
+        return original_func(func.__wrapped__)
+    return func
\ No newline at end of file
diff --git a/seamless/internal/validation.py b/seamless/internal/validation.py
index e5c362e..5f6b5d6 100644
--- a/seamless/internal/validation.py
+++ b/seamless/internal/validation.py
@@ -1,14 +1,12 @@
 # type: ignore
 
-from typing import Any, TYPE_CHECKING
+from typing import Any
+
+from pydom.context import Context
 
 from .utils import Promise, _obj, wraps
 from ..errors import ClientError
 
-if TYPE_CHECKING:
-    from ..context import Context
-
-
 class _DataValidationError(ClientError): ...
 
 
diff --git a/seamless/middlewares/__init__.py b/seamless/middlewares/__init__.py
deleted file mode 100644
index fb8bdee..0000000
--- a/seamless/middlewares/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .asgi import ASGIMiddleware
-
-__all__ = ["ASGIMiddleware"]
diff --git a/seamless/middlewares/asgi.py b/seamless/middlewares/asgi.py
deleted file mode 100644
index 3db0020..0000000
--- a/seamless/middlewares/asgi.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from socketio import ASGIApp
-
-from .base import BaseMiddleware
-from ..context.request import HTTPRequest
-from ..internal.constants import CLAIM_COOKIE_NAME
-
-
-class ASGIMiddleware(BaseMiddleware):
-    async def __call__(self, scope, receive, send):
-        async def _send(message):
-            await send(message)
-
-        if scope["type"] == "http":
-            request = HTTPRequest.make(scope, type="asgi")
-
-            async def _send(message):
-                if message["type"] != "http.response.start":
-                    return await send(message)
-
-                if self._is_render_request():
-                    if "headers" not in message:
-                        message["headers"] = []
-
-                    message["headers"].append(
-                        self._make_cookie_header(CLAIM_COOKIE_NAME, request.id)
-                    )
-
-                elif request.path.startswith(self.socket_path):
-                    if CLAIM_COOKIE_NAME in request.cookies:
-                        if "headers" not in message:
-                            message["headers"] = []
-
-                        message["headers"].append(
-                            self._remove_cookie(CLAIM_COOKIE_NAME)
-                        )
-
-                await send(message)
-
-        await self.app(scope, receive, _send)
-
-    def _app_class(self):
-        return ASGIApp
diff --git a/seamless/middlewares/base.py b/seamless/middlewares/base.py
deleted file mode 100644
index 88d049c..0000000
--- a/seamless/middlewares/base.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from ..context.context import Context, get_context
-from ..context.request import request as _request
-
-
-class BaseMiddleware:
-    def __init__(self, app, socket_path="/socket.io", context: Context | None = None):
-        if context is None:
-            context = get_context()
-        self.socket_path = f"/{socket_path.strip('/')}"
-        self.app = self._app_class()(context.server, app, socketio_path=socket_path)
-
-    def _app_class(self):
-        raise NotImplementedError("self._app_class is not implemented")
-
-    def _make_cookie_header(
-        self,
-        name: str,
-        value,
-        *,
-        expires: str | None = None,
-        path: str | None = None,
-        http_only: bool | None = None,
-        domain: str | None = None,
-        same_site: str | None = None,
-        max_age: int | None = None,
-        secure: bool | None = None,
-    ):
-        if path is None:
-            path = self.socket_path
-        cookie_value = f"{name}={value};"
-        if max_age:
-            cookie_value += f" Max-Age={max_age};"
-        if expires:
-            cookie_value += f" Expires={expires};"
-        if path:
-            cookie_value += f" Path={path};"
-        if domain:
-            cookie_value += f" Domain={domain};"
-        if secure:
-            cookie_value += " Secure;"
-        if http_only:
-            cookie_value += " HttpOnly;"
-        if same_site:
-            cookie_value += f" SameSite={same_site};"
-
-        return (b"Set-Cookie", cookie_value.encode())
-
-    def _remove_cookie(self, name: str, path: str | None = None):
-        if path is None:
-            path = self.socket_path
-        return (
-            b"Set-Cookie",
-            f"{name}=; Path={path}; Expires=Thu, 01 Jan 1970 00:00:00 GMT;".encode(),
-        )
-
-    def _is_render_request(self):
-        request = _request()
-        if not request:
-            return False
-        return request.id is not None
diff --git a/seamless/rendering/__init__.py b/seamless/rendering/__init__.py
index 0a8afac..a4ce2da 100644
--- a/seamless/rendering/__init__.py
+++ b/seamless/rendering/__init__.py
@@ -1,57 +1,5 @@
-from typing import TYPE_CHECKING, overload, Literal
+from pydom import render
+from pydom.rendering.tree import build_tree
+from pydom.rendering.tree.nodes import ContextNode, ElementNode, TextNode, TreeNode
 
-from .html import render_html
-from .json import render_json
-
-if TYPE_CHECKING:
-    from ..types import Renderable, Primitive
-    from ..context import Context
-
-
-@overload
-def render(
-    element: "Renderable | Primitive",
-    *,
-    to: Literal["html"],
-    pretty: bool = False,
-    tab_indent: int = 1,
-    context: "Context | None" = None,
-    **render_state_data,
-) -> str: ...
-
-
-@overload
-def render(
-    element: "Renderable | Primitive",
-    *,
-    to: Literal["json"],
-    context: "Context | None" = None,
-    **render_state_data,
-) -> dict: ...
-
-
-@overload
-def render(
-    element: "Renderable | Primitive",
-    *,
-    pretty: bool = False,
-    tab_indent: int = 1,
-    context: "Context | None" = None,
-    **render_state_data,
-) -> str: ...
-
-
-def render(
-    element: "Renderable | Primitive",
-    *,
-    to: Literal["json", "html"] = "html",
-    **kwargs,
-) -> str | dict:
-
-    if to == "json":
-        return render_json(element, **kwargs)
-    if to == "html":
-        return render_html(element, **kwargs)
-
-
-__all__ = ["render", "render_html", "render_json"]
\ No newline at end of file
+__all__ = ["ContextNode", "ElementNode", "TextNode", "TreeNode", "build_tree", "render"]
diff --git a/seamless/rendering/html.py b/seamless/rendering/html.py
deleted file mode 100644
index 48f004c..0000000
--- a/seamless/rendering/html.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from typing import TYPE_CHECKING
-from uuid import uuid4 as uuid
-
-from ..context.request import request as _request
-from ..errors import RenderError
-from .props import render_props
-from .render_state import RenderState
-from .tree import ElementNode, TreeNode, TextNode, build_tree
-
-if TYPE_CHECKING:
-    from ..types import Renderable, Primitive
-    from ..context import Context
-
-
-def render_html(
-    element: "Renderable | Primitive",
-    *,
-    pretty=False,
-    tab_indent=1,
-    context: "Context | None" = None,
-    **render_state_data,
-) -> str:
-    """
-    Renders the given element into an HTML string.
-
-    Args:
-        element (Renderable | Primitive): The element to be rendered.
-        pretty (bool, optional): Whether to format the HTML string with indentation and line breaks. Defaults to False.
-        tab_indent (int, optional): The number of spaces to use for indentation when pretty is True. Defaults to 1.
-
-    Returns:
-        str: The rendered HTML string.
-    """
-    from ..context import get_context
-
-    request = _request()
-    if request is not None:
-        request.id = str(uuid())
-
-    context = get_context(context)
-    render_state = RenderState(root=element, render_target="html", **render_state_data)
-    context.injector.add(RenderState, render_state)
-
-    tree = build_tree(element, context=context)
-
-    return _render_html(tree, pretty=pretty, tab_indent=tab_indent)
-
-
-def _render_html(node: TreeNode, pretty=False, tab_indent=0):
-    tab = "  " * tab_indent if pretty else ""
-    newline = "\n" if pretty else ""
-
-    if isinstance(node, TextNode):
-        return tab + node.text
-
-    if not isinstance(node, ElementNode):
-        raise RenderError("Invalid node type")
-
-    props_string = render_props(node.props)
-    open_tag = f"{node.tag_name} {props_string}".strip()
-
-    if node.children is None:
-        return tab + f"<{open_tag}>"
-
-    children = newline.join(
-        _render_html(child, pretty=pretty, tab_indent=tab_indent + 1)
-        for child in node.children
-    )
-
-    if not node.tag_name:
-        return tab + children
-
-    return (
-        tab + f"<{open_tag}>{newline if children else ''}{children}</{node.tag_name}>"
-    )
diff --git a/seamless/rendering/json.py b/seamless/rendering/json.py
deleted file mode 100644
index dd62d2c..0000000
--- a/seamless/rendering/json.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import TYPE_CHECKING
-
-from ..errors import RenderError
-from .render_state import RenderState
-from .tree import build_tree, TreeNode, TextNode, ElementNode
-
-if TYPE_CHECKING:
-    from ..types import Renderable, Primitive
-    from ..context import Context
-
-
-def render_json(
-    element: "Renderable | Primitive",
-    *,
-    context: "Context | None" = None,
-    **render_state_data,
-):
-    from ..context import get_context
-
-    context = get_context(context)
-    render_state = RenderState(root=element, render_target="json", **render_state_data)
-    context.injector.add(RenderState, render_state)
-
-    tree = build_tree(element, context=context)
-
-    return _render_json(tree)
-
-
-def _render_json(node: TreeNode):
-    if isinstance(node, TextNode):
-        return node.text
-
-    if not isinstance(node, ElementNode):
-        raise RenderError("Invalid node type")
-
-    children = []
-    if node.children is not None:
-        for child in node.children:
-            children.append(_render_json(child))
-
-    return {
-        "type": node.tag_name,
-        "props": node.props,
-        "children": children,
-    }
diff --git a/seamless/rendering/props.py b/seamless/rendering/props.py
deleted file mode 100644
index 28d4344..0000000
--- a/seamless/rendering/props.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from html import escape
-from typing import Any, TYPE_CHECKING
-
-from ..errors import RenderError
-
-if TYPE_CHECKING:
-    from ..context import Context
-    from .tree import ElementNode
-
-
-def transform_props(element: "ElementNode", *, context: "Context"):
-    for matcher, transformer in context.prop_transformers:
-        if isinstance(matcher, str):
-            key = matcher
-            if key in element.props:
-                value = element.props[key]
-                if callable(transformer):
-                    transformer(key, value, element)
-                else:
-                    element.props[transformer] = value
-        elif callable(matcher):
-            for key, value in list(element.props.items()):
-                if matcher(key, value):
-                    transformer(key, value, element)
-        else:
-            raise RenderError(
-                f"Invalid matcher: {matcher} must be a callable or a string."
-            )
-
-    return {
-        key: value
-        for key, value in element.props.items()
-        if value is not None
-    }
-
-
-def render_props(props: dict[str, Any]) -> str:
-    return " ".join(
-        key if value is True else f'{key}="{escape(str(value))}"'
-        for key, value in props.items()
-    )
diff --git a/seamless/rendering/render_state.py b/seamless/rendering/render_state.py
deleted file mode 100644
index be73d36..0000000
--- a/seamless/rendering/render_state.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Literal, TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from ..types import Renderable, Primitive
-
-
-class RenderState:
-    def __init__(self, *, root: "Renderable | Primitive", render_target: Literal["json", "html"], **kwargs):
-        self.root = root
-        self.render_target = render_target
-        self.custom_data = kwargs
diff --git a/seamless/rendering/transformers/__init__.py b/seamless/rendering/transformers/__init__.py
index c21796b..4d1f86c 100644
--- a/seamless/rendering/transformers/__init__.py
+++ b/seamless/rendering/transformers/__init__.py
@@ -1,4 +1,3 @@
-from .post_render_transformer import post_render_transformer
-from .property_transformer import property_transformer
+from pydom.rendering.transformers import *
 
-__all__ = ["post_render_transformer", "property_transformer"]
\ No newline at end of file
+__all__ = ["post_render_transformer", "property_transformer"]
diff --git a/seamless/rendering/transformers/post_render_transformer.py b/seamless/rendering/transformers/post_render_transformer.py
deleted file mode 100644
index 9591dbe..0000000
--- a/seamless/rendering/transformers/post_render_transformer.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from ...context.context import PostRenderTransformer, get_context, Context
-
-
-def post_render_transformer(context: Context | None = None):
-    """
-    A decorator to register a post-render transformer.
-
-    Post-render transformers are functions that take the rendered tree and can modify it in place.
-
-    Args:
-        context: The context to register the transformer with.
-
-    Returns:
-        A decorator that takes a transformer function and registers it.
-
-    Example:
-        >>> @post_render_transformer()
-        ... def add_custom_script(root: TreeNode) -> TreeNode:
-        ...     root.get_by_tag("head").append_child(
-        ...         ElementNode(
-        ...             tag_name="script",
-        ...             props={"src": "/custom.js"},
-        ...         )
-        ...     )
-
-    """
-    context = get_context(context)
-
-    def decorator(func: PostRenderTransformer):
-        context.add_post_render_transformer(func)
-        return func
-
-    return decorator
diff --git a/seamless/rendering/transformers/property_transformer.py b/seamless/rendering/transformers/property_transformer.py
deleted file mode 100644
index 2459194..0000000
--- a/seamless/rendering/transformers/property_transformer.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Any, Callable
-from ...context.context import PropertyTransformer, Context, get_context
-
-
-def property_transformer(
-    matcher: Callable[[str, Any], bool] | str, context: Context | None = None
-):
-    """
-    A decorator to register a property transformer.
-
-    Transformers are functions that take a key, a value, and the element node object.
-
-    After handling the key and value, the transformer should update the element node
-    properties in place.
-
-    Args:
-        matcher: A callable that takes a key and a value and returns a boolean
-        indicating whether the transformer should be applied.
-        If a string is provided, it is assumed to be a key that should be matched exactly.
-
-    Returns:
-        A decorator that takes a transformer function and registers it.
-
-    Example:
-        >>> @property_transformer("class_name")
-        ... def class_name_mapper(key, value, element):
-        ...     if isinstance(class_name, list):
-        ...         class_name = " ".join(class_name)
-        ...
-        ...     element.props["class"] = " ".join(str(class_name).split())
-
-    """
-    context = get_context(context)
-
-    def decorator(func: PropertyTransformer):
-        context.add_prop_transformer(matcher, func)
-        return func
-
-    return decorator
diff --git a/seamless/rendering/tree/__init__.py b/seamless/rendering/tree/__init__.py
deleted file mode 100644
index c19367d..0000000
--- a/seamless/rendering/tree/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .tree import build_tree, TreeNode, TextNode, ElementNode
-
-__all__ = ["build_tree", "TreeNode", "TextNode", "ElementNode"]
diff --git a/seamless/rendering/tree/tree.py b/seamless/rendering/tree/tree.py
deleted file mode 100644
index 27c3f95..0000000
--- a/seamless/rendering/tree/tree.py
+++ /dev/null
@@ -1,131 +0,0 @@
-from typing import TYPE_CHECKING
-
-from ..props import transform_props
-
-
-from ...core.component import Component
-from ...element import Element
-from ...errors import RenderError
-from ...internal.utils import is_primitive
-
-if TYPE_CHECKING:
-    from ...types import Primitive, Renderable
-    from ...context import Context
-
-
-class TreeNode:
-    def __init__(self, parent: "ElementNode | None" = None):
-        self.parent = parent
-
-
-class ElementNode(TreeNode):
-    def __init__(
-        self,
-        tag_name: str,
-        props: dict | None = None,
-        children: list[TreeNode] | None = None,
-        parent: "ElementNode | None" = None,
-    ):
-        super().__init__(parent=parent)
-        self.tag_name = tag_name
-        self.props = dict(props or ())
-        self.children = children
-
-    def get_by_tag(self, tag: str):
-        def _get_by_tag(node: ElementNode) -> ElementNode | None:
-            if node.tag_name == tag:
-                return node
-            if node.children is not None:
-                for child in node.children:
-                    if isinstance(child, ElementNode):
-                        result = _get_by_tag(child)
-                        if result:
-                            return result
-            return None
-
-        return _get_by_tag(self)
-
-    def get_all_by_tag(self, tag: str):
-        def _get_all_by_tag(node: ElementNode) -> list[ElementNode]:
-            result = []
-            if node.tag_name == tag:
-                result.append(node)
-            if node.children is not None:
-                for child in node.children:
-                    if isinstance(child, ElementNode):
-                        result.extend(_get_all_by_tag(child))
-            return result
-
-        return _get_all_by_tag(self)
-
-    def insert_after(self, node: TreeNode):
-        if self.parent is None:
-            raise RenderError("Cannot insert after root element")
-
-        assert isinstance(self.parent.children, list)
-
-        current_index = self.parent.children.index(self)
-        self.parent.children.insert(current_index + 1, node)
-
-    def insert_before(self, node: TreeNode):
-        if self.parent is None:
-            raise RenderError("Cannot insert before root element")
-
-        assert isinstance(self.parent.children, list)
-
-        current_index = self.parent.children.index(self)
-        self.parent.children.insert(current_index, node)
-
-    def insert_child(self, index: int, node: TreeNode):
-        if self.children is None:
-            raise RenderError("Cannot append child to inline elements")
-        
-        self.children.insert(index, node)
-
-    def append_child(self, node: TreeNode):
-        if self.children is None:
-            raise RenderError("Cannot append child to inline elements")
-        
-        self.children.append(node)
-
-
-class TextNode(TreeNode):
-    def __init__(self, text: "Primitive", parent: ElementNode | None = None):
-        super().__init__(parent=parent)
-        self.text = str(text) if text is not None else ""
-
-
-def build_raw_tree(
-    renderable: "Renderable | Primitive", *, context: "Context"
-) -> TreeNode:
-    while isinstance(renderable, Component):
-        renderable = renderable.render()
-
-    if is_primitive(renderable):
-        return TextNode(renderable)
-
-    assert isinstance(renderable, Element)
-    children = (
-        [build_raw_tree(child, context=context) for child in renderable.children]
-        if not renderable.inline
-        else None
-    )
-    element = ElementNode(
-        tag_name=renderable.tag_name, props=renderable.props, children=children
-    )
-
-    transform_props(element, context=context)
-
-    return element
-
-
-def build_tree(renderable: "Renderable | Primitive", *, context: "Context") -> TreeNode:
-    tree = build_raw_tree(renderable, context=context)
-
-    if not isinstance(tree, ElementNode):
-        return tree
-
-    for transformer in context.post_render_transformers:
-        transformer(tree)
-
-    return tree
diff --git a/seamless/styling/__init__.py b/seamless/styling/__init__.py
index 238de75..de7899e 100644
--- a/seamless/styling/__init__.py
+++ b/seamless/styling/__init__.py
@@ -1,5 +1,1 @@
-from .color import Color
-from .style import StyleObject
-from .css_modules import CSS
-
-__all__ = ["Color", "StyleObject", "CSS"]
\ No newline at end of file
+from pydom.styling import *
diff --git a/seamless/styling/color.py b/seamless/styling/color.py
deleted file mode 100644
index 22dcfdd..0000000
--- a/seamless/styling/color.py
+++ /dev/null
@@ -1,186 +0,0 @@
-class Color(dict):
-  # region: Pre-defined colors
-  BLACK: 'Color'
-  BLUE: 'Color'
-  DARK_BLUE: 'Color'
-  MIDNIGHT_BLUE: 'Color'
-  DARK_SLATE_BLUE: 'Color'
-  SLATE_BLUE: 'Color'
-  MEDIUM_SLATE_BLUE: 'Color'
-  MEDIUM_BLUE: 'Color'
-  ROYAL_BLUE: 'Color'
-  CORNFLOWER_BLUE: 'Color'
-  DODGER_BLUE: 'Color'
-  DEEP_SKY_BLUE: 'Color'
-  LIGHT_SKY_BLUE: 'Color'
-  SKY_BLUE: 'Color'
-  LIGHT_BLUE: 'Color'
-  STEEL_BLUE: 'Color'
-  ALICE_BLUE: 'Color'
-  WHITE: 'Color'
-  GAINSBORO: 'Color'
-  LIGHT_GREY: 'Color'
-  SILVER: 'Color'
-  DIM_GREY: 'Color'
-  GREY: 'Color'
-  DARK_GREY: 'Color'
-  SLATE_GREY: 'Color'
-  LIGHT_SLATE_GREY: 'Color'
-  LIGHT_STEEL_BLUE: 'Color'
-  YELLOW: 'Color'
-  ORANGE: 'Color'
-  RED: 'Color'
-  BROWN: 'Color'
-  WHITE_SMOKE: 'Color'
-  GREEN: 'Color'
-  DARK_SLATE_GREY: 'Color'
-  LAVENDER: 'Color'
-  MEDIUM_PURPLE: 'Color'
-  VIOLET: 'Color'
-  PLUM: 'Color'
-  THISTLE: 'Color'
-  LAVENDER_BLUSH: 'Color'
-  MISTY_ROSE: 'Color'
-  ANTIQUE_WHITE: 'Color'
-  LINEN: 'Color'
-  BEIGE: 'Color'
-  PINK: 'Color'
-  # endregion
-
-  def __init__(self, r, g, b, a=1):
-    self.r = r
-    self.g = g
-    self.b = b
-    self.a = a
-
-  @classmethod
-  def from_hex(cls, hex):
-    if hex.startswith("#"):
-      hex = hex[1:]
-    if len(hex) == 3:
-      hex = "".join([c*2 for c in hex])
-    if len(hex) != 6:
-      raise ValueError("Invalid hex color")
-    return cls(*[int(hex[i:i+2], 16) for i in range(0, 6, 2)])
-  
-  @classmethod
-  def from_rgb(cls, r, g, b):
-    return cls(r, g, b)
-  
-  @classmethod
-  def from_rgba(cls, r, g, b, a):
-    return cls(r, g, b, a)
-  
-  @classmethod
-  def from_hsl(cls, h, s, l):
-    return cls(*Color.hsl_to_rgb(h, s, l))
-  
-  @classmethod
-  def from_hsla(cls, h, s, l, a):
-    return cls(*Color.hsl_to_rgb(h, s, l), a)
-  
-  def to_hex(self):
-    return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
-  
-  def to_rgb(self):
-    return (self.r, self.g, self.b)
-  
-  def to_rgba(self):
-    return (self.r, self.g, self.b, self.a)
-  
-  def to_hsl(self):
-    return Color.rgb_to_hsl(self.r, self.g, self.b)
-  
-  def to_hsla(self):
-    return (*Color.rgb_to_hsl(self.r, self.g, self.b), self.a)
-  
-  @staticmethod
-  def rgb_to_hsl(r, g, b):
-    r /= 255
-    g /= 255
-    b /= 255
-    cmax = max(r, g, b)
-    cmin = min(r, g, b)
-    delta = cmax - cmin
-    l = (cmax + cmin) / 2
-    if delta == 0:
-      h = 0
-      s = 0
-    else:
-      if cmax == r:
-        h = ((g - b) / delta) % 6
-      elif cmax == g:
-        h = (b - r) / delta + 2
-      else:
-        h = (r - g) / delta + 4
-      s = delta / (1 - abs(2*l - 1))
-    return (h*60, s, l)
-  
-  @staticmethod
-  def hsl_to_rgb(h, s, l):
-    c = (1 - abs(2*l - 1)) * s
-    x = c * (1 - abs((h / 60) % 2 - 1))
-    m = l - c/2
-    if h < 60:
-      r, g, b = c, x, 0
-    elif h < 120:
-      r, g, b = x, c, 0
-    elif h < 180:
-      r, g, b = 0, c, x
-    elif h < 240:
-      r, g, b = 0, x, c
-    elif h < 300:
-      r, g, b = x, 0, c
-    else:
-      r, g, b = c, 0, x
-    return (int((r+m)*255), int((g+m)*255), int((b+m)*255))
-  
-  def __str__(self):
-    return self.to_hex()
-
-Color.BLACK = Color.from_hex("#000000")
-Color.BLUE = Color.from_hex("#0000ff")
-Color.DARK_BLUE = Color.from_hex("#00008b")
-Color.MIDNIGHT_BLUE = Color.from_hex("#191970")
-Color.DARK_SLATE_BLUE = Color.from_hex("#483d8b")
-Color.SLATE_BLUE = Color.from_hex("#6a5acd")
-Color.MEDIUM_SLATE_BLUE = Color.from_hex("#7b68ee")
-Color.MEDIUM_BLUE = Color.from_hex("#0000cd")
-Color.ROYAL_BLUE = Color.from_hex("#4169e1")
-Color.CORNFLOWER_BLUE = Color.from_hex("#6495ed")
-Color.DODGER_BLUE = Color.from_hex("#1e90ff")
-Color.DEEP_SKY_BLUE = Color.from_hex("#00bfff")
-Color.LIGHT_SKY_BLUE = Color.from_hex("#87cefa")
-Color.SKY_BLUE = Color.from_hex("#87ceeb")
-Color.LIGHT_BLUE = Color.from_hex("#add8e6")
-Color.STEEL_BLUE = Color.from_hex("#4682b4")
-Color.ALICE_BLUE = Color.from_hex("#f0f8ff")
-Color.WHITE = Color.from_hex("#ffffff")
-Color.GAINSBORO = Color.from_hex("#dcdcdc")
-Color.LIGHT_GREY = Color.from_hex("#d3d3d3")
-Color.SILVER = Color.from_hex("#c0c0c0")
-Color.DIM_GREY = Color.from_hex("#696969")
-Color.GREY = Color.from_hex("#808080")
-Color.DARK_GREY = Color.from_hex("#a9a9a9")
-Color.SLATE_GREY = Color.from_hex("#708090")
-Color.LIGHT_SLATE_GREY = Color.from_hex("#778899")
-Color.LIGHT_STEEL_BLUE = Color.from_hex("#b0c4de")
-Color.YELLOW = Color.from_hex("#ffff00")
-Color.ORANGE = Color.from_hex("#ffa500")
-Color.RED = Color.from_hex("#ff0000")
-Color.BROWN = Color.from_hex("#a52a2a")
-Color.WHITE_SMOKE = Color.from_hex("#f5f5f5")
-Color.GREEN = Color.from_hex("#00ff00")
-Color.DARK_SLATE_GREY = Color.from_hex("#2f4f4f")
-Color.LAVENDER = Color.from_hex("#e6e6fa")
-Color.MEDIUM_PURPLE = Color.from_hex("#9370db")
-Color.VIOLET = Color.from_hex("#ee82ee")
-Color.PLUM = Color.from_hex("#dda0dd")
-Color.THISTLE = Color.from_hex("#d8bfd8")
-Color.LAVENDER_BLUSH = Color.from_hex("#fff0f5")
-Color.MISTY_ROSE = Color.from_hex("#ffe4e1")
-Color.ANTIQUE_WHITE = Color.from_hex("#faebd7")
-Color.LINEN = Color.from_hex("#faf0e6")
-Color.BEIGE = Color.from_hex("#f5f5dc")
-Color.PINK = Color.from_hex("#ffc0cb")
-
diff --git a/seamless/styling/css_modules.py b/seamless/styling/css_modules.py
deleted file mode 100644
index d910e41..0000000
--- a/seamless/styling/css_modules.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import inspect
-from os import PathLike
-from pathlib import Path
-import re
-import cssutils
-from ..internal.utils import short_uuid
-
-
-class CSSClass:
-    def __init__(self, class_name: str):
-        self.class_name = class_name
-        self.sub_rules: dict[str, dict[str, str]] = {}
-        self.uuid = short_uuid()
-
-    def add_rule(self, rule: str, properties: dict[str, str]):
-        if rule not in self.sub_rules:
-            self.sub_rules[rule] = {}
-
-        self.sub_rules[rule].update(properties)
-
-    def to_css_string(self, minified=False):
-        rules = []
-        rules_format_string = (
-            ".{0}{1} {{\n{2}}}\n" if not minified else ".{0}{1}{{{2}}}"
-        )
-        single_rule_format_string = "\t{0}: {1}\n" if not minified else "{0}:{1}"
-        for key, rule in self.sub_rules.items():
-            rule_text = ""
-            for attr, value in rule.items():
-                value = value.strip()
-                if not value.endswith(";"):
-                    value += ";"
-                rule_text += single_rule_format_string.format(attr, value)
-
-            rules.append(rules_format_string.format(self.uuid, key, rule_text))
-
-        join_string = "" if minified else "\n"
-        return join_string.join(rules)
-
-    def __str__(self):
-        return self.uuid
-
-
-class CSSModule:
-    def __init__(self, module_name):
-        css = cssutils.parseFile(module_name)
-        self.module_name = module_name
-        self.classes: dict[str, CSSClass] = {}
-        self.raw_css = ""
-        for rule in css:
-            if rule.type == rule.STYLE_RULE:
-                selectors = rule.selectorList
-                first_selector = selectors[0]
-
-                # if the first selector starts with @ add it to the global styles
-                if first_selector.seq[0].type == "at-keyword":
-                    continue
-
-                if first_selector.seq[0].type != "class":
-                    continue
-
-                style_dict = {
-                    css_property.name: css_property.value for css_property in rule.style
-                }
-
-                base_name = first_selector.seq[0].value.removeprefix(".")
-                css_class = (
-                    self.classes[base_name]
-                    if base_name in self.classes
-                    else CSSClass(base_name)
-                )
-                for selector in selectors:
-                    css_class.add_rule(
-                        selector.selectorText.replace(f".{base_name}", ""), style_dict
-                    )
-
-                self.classes[base_name] = css_class
-            
-            if rule.type == rule.UNKNOWN_RULE:
-                self.raw_css += rule.cssText
-
-    def __getattr__(self, __name: str):
-        if __name not in self.classes:
-            raise AttributeError(f"CSS class {__name} not found in {self.module_name}")
-        return self.classes[__name].uuid
-
-    def to_css_string(self, minified=False):
-        raw_css = self.raw_css.replace("\n", "").replace("\t", "") if minified else self.raw_css
-        raw_css = re.sub(r"\s+", " ", raw_css) if minified else raw_css
-        join_string = "" if minified else "\n\n"
-        return join_string.join(
-            [*(css_class.to_css_string(minified) for css_class in self.classes.values()), raw_css]
-        )
-
-
-class CSSModulesManager:
-    def __init__(self):
-        self.modules: dict[str, CSSModule] = {}
-        self.folder: Path = Path.cwd()
-
-    def module(self, css_path: PathLike | str):
-        """
-        Get a CSS module by its path.
-        This will return a CSSModule object.
-        """
-        css_path = str(self._full_path(css_path).resolve())
-        if css_path not in self.modules:
-            self.modules[css_path] = CSSModule(css_path)
-        return self.modules[css_path]
-
-    def to_css_string(self, minified=False):
-        """
-        Get the CSS output of all the CSS modules.
-        """
-        join_string = "" if minified else "\n\n"
-        return join_string.join(
-            css_module.to_css_string(minified) for css_module in self.modules.values()
-        )
-
-    def set_root_folder(self, folder: PathLike | str):
-        """
-        Set the folder where the CSS files are located.
-        """
-        folder = Path(folder)
-
-        self.folder = folder.absolute()
-
-    def _full_path(self, css_path: PathLike | str) -> Path:
-        if isinstance(css_path, str):
-            if css_path.startswith("./"):
-                frame = inspect.stack()[2]
-                module = inspect.getmodule(frame[0])
-                if module is None or module.__file__ is None:
-                    raise ValueError("Cannot use relative path in a module without a file")
-                
-                css_path = Path(module.__file__).parent / css_path
-
-        css_path = Path(css_path)
-
-        if not css_path.is_absolute():
-            return self.folder / css_path
-        
-        return css_path
-
-
-CSS = CSSModulesManager()
-
-__all__ = ["CSS"]
diff --git a/seamless/types/__init__.py b/seamless/types/__init__.py
index 5d31e47..ef29ee8 100644
--- a/seamless/types/__init__.py
+++ b/seamless/types/__init__.py
@@ -1,11 +1,1 @@
-from typing import Collection, TypeAlias
-
-from ..core import Component
-from ..element import Element
-
-Primitive: TypeAlias = str | int | float | bool | None
-Renderable: TypeAlias = Element | Component
-
-ChildType: TypeAlias = Renderable | Primitive
-RenderResult: TypeAlias = Renderable | Primitive
-ChildrenType: TypeAlias = Collection[ChildType]
+from pydom.types import *
diff --git a/seamless/types/events.py b/seamless/types/events.py
index 7e0c477..a903af1 100644
--- a/seamless/types/events.py
+++ b/seamless/types/events.py
@@ -16,7 +16,7 @@ class BaseModel: ...
 
 class EventTarget(BaseModel):
     id: str
-    tagName: str
+    tag_name: str
 
 
 class Event(BaseModel):
@@ -71,7 +71,9 @@ class FormDataEvent(Event): ...
 class GamepadEvent(Event): ...
 
 
-class HashChangeEvent(Event): ...
+class HashChangeEvent(Event):
+    old_url: str
+    new_url: str
 
 
 class HIDInputReportEvent(Event): ...
@@ -86,7 +88,12 @@ class InputEvent(Event): ...
 class KeyboardEvent(Event): ...
 
 
-class MessageEvent(Event): ...
+class MessageEvent(Event):
+    data: str
+    origin: str
+    last_event_id: str
+    source: EventTarget
+    ports: list
 
 
 class MouseEvent(Event): ...
@@ -104,7 +111,8 @@ class PaymentRequestUpdateEvent(Event): ...
 class PointerEvent(Event): ...
 
 
-class PopStateEvent(Event): ...
+class PopStateEvent(Event):
+    state: dict | None
 
 
 class ProgressEvent(Event): ...
@@ -116,7 +124,11 @@ class RTCDataChannelEvent(Event): ...
 class RTCPeerConnectionIceEvent(Event): ...
 
 
-class StorageEvent(Event): ...
+class StorageEvent(Event):
+    key: str
+    old_value: str
+    new_value: str
+    url: str
 
 
 class SubmitEvent(Event, Generic[T]):
@@ -190,4 +202,4 @@ class WheelEvent(Event): ...
     "UIEvent",
     "WebGLContextEvent",
     "WheelEvent",
-]
\ No newline at end of file
+]
diff --git a/seamless/types/html/html_body_element.py b/seamless/types/html/html_body_element.py
index a7470c1..cd85930 100644
--- a/seamless/types/html/html_body_element.py
+++ b/seamless/types/html/html_body_element.py
@@ -1,5 +1,117 @@
 from seamless.types.html.html_element_props import HTMLElementProps
+from seamless.types.html.html_event_props import Event, EventFunction
+from seamless.types.events import (
+    BeforeUnloadEvent,
+    HashChangeEvent,
+    MessageEvent,
+    PopStateEvent,
+    StorageEvent,
+)
 
 
-class HTMLBodyElement(HTMLElementProps, total=False):
+class HTMLBodyElement(HTMLElementProps, total=False, closed=False):
+    alink: str
+    """
+    **@deprecated** Use the CSS color property in conjunction with the :active pseudo-class instead
+
+    Specifies the color of text for hyperlinks when selected
+    """
     background: str
+    """
+    **@deprecated** Use the CSS background property on the element instead
+
+    Specifies the URI of an image to use as a background
+    """
+    bgcolor: str
+    """
+    **@deprecated** Use the CSS background-color property on the element instead
+
+    Specifies the background color of the document
+    """
+    bottom_margin: int
+    """
+    **@deprecated** Use the CSS margin-bottom property on the element instead
+
+    Specifies the bottom margin of the body
+    """
+    left_margin: int
+    """
+    **@deprecated** Use the CSS margin-left property on the element instead
+
+    Specifies the left margin of the body
+    """
+    link: str
+    """
+    **@deprecated** Use the CSS color property in conjunction with the :link pseudo-class instead
+
+    Specifies the color of text for unvisited hypertext links
+    """
+    on_after_print: EventFunction[Event]
+    """
+    Function to call after the user has printed the document
+    """
+    on_before_print: EventFunction[Event]
+    """
+    Function to call before the user prints the document
+    """
+    on_before_unload: EventFunction[BeforeUnloadEvent]
+    """
+    Function to call when the document is about to be unloaded
+    """
+    on_hash_change: EventFunction[HashChangeEvent]
+    """
+    Function to call when the fragment identifier part (starting with the hash (`#`) character)
+    of the document's current address has changed
+    """
+    on_language_change: EventFunction[Event]
+    """
+    Function to call when the preferred languages changed
+    """
+    on_message: EventFunction[MessageEvent]
+    """
+    Function to call when the document has received a message
+    """
+    on_offline: EventFunction[Event]
+    """
+    Function to call when network communication has failed
+    """
+    on_online: EventFunction[Event]
+    """
+    Function to call when network communication has been restored
+    """
+    on_pop_state: EventFunction[PopStateEvent]
+    """
+    Function to call when the user has navigated session history
+    """
+    on_storage: EventFunction[StorageEvent]
+    """
+    Function to call when the storage area has changed
+    """
+    on_unload: EventFunction[Event]
+    """
+    Function to call when the document is going away
+    """
+    right_margin: int
+    """
+    **@deprecated** Use the CSS margin-right property on the element instead
+
+    Specifies the right margin of the body
+    """
+    text: str
+    """
+    **@deprecated** Use the CSS color property on the element instead
+
+    Specifies the color of text
+    """
+    top_margin: int
+    """
+    **@deprecated** Use the CSS margin-top property on the element instead
+
+    Specifies the top margin of the body
+    """
+    vlink: str
+    """
+    **@deprecated** Use the CSS color property in conjunction with the :visited pseudo-class instead
+
+    Specifies the color of text for visited hypertext links
+    """
diff --git a/seamless/types/html/html_element.py b/seamless/types/html/html_element.py
index 929b32a..e76dc21 100644
--- a/seamless/types/html/html_element.py
+++ b/seamless/types/html/html_element.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Iterable, Union
+from typing import TYPE_CHECKING, Iterable, Union, Literal
 
 from typing_extensions import TypedDict
 
@@ -13,7 +13,7 @@ class HTMLElement(TypedDict, total=False, closed=False):
     class_name: str | Iterable[str]
     content_editable: str
     # data: dict[str, str]  # add this if needed in the future
-    dir: str
+    dir: Literal["ltr", "rtl", "auto"]
     draggable: str
     hidden: str
     id: str
diff --git a/seamless/types/html/html_head_element.py b/seamless/types/html/html_head_element.py
index ae69e38..267c678 100644
--- a/seamless/types/html/html_head_element.py
+++ b/seamless/types/html/html_head_element.py
@@ -1,5 +1,5 @@
 from seamless.types.html.html_element_props import HTMLElementProps
 
 
-class HTMLHeadElement(HTMLElementProps, total=False):
-    pass  # No additional attributes
+class HTMLHeadElement(HTMLElementProps, total=False, closed=False):
+    profile: str
diff --git a/seamless/types/html/html_html_element.py b/seamless/types/html/html_html_element.py
index 4b6ecc6..b48b892 100644
--- a/seamless/types/html/html_html_element.py
+++ b/seamless/types/html/html_html_element.py
@@ -2,4 +2,5 @@
 
 
 class HTMLHtmlElement(HTMLElementProps, total=False):
-    pass  # No additional attributes
+    version: str
+    xmlns: str
diff --git a/seamless/version.py b/seamless/version.py
index ec66d55..45b352a 100644
--- a/seamless/version.py
+++ b/seamless/version.py
@@ -1,1 +1,1 @@
-version = "0.9.1"
\ No newline at end of file
+version = "0.9.2"
\ No newline at end of file


Test patch:
diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml
index 7ced67a..60bf3f6 100644
--- a/.github/workflows/python-test.yml
+++ b/.github/workflows/python-test.yml
@@ -15,20 +15,31 @@ permissions:
 jobs:
   test:
     runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.11", "3.12"]
 
     steps:
       - uses: actions/checkout@v4
-      - name: Set up Python
+      - name: Set up Python ${{ matrix.python-version }}
         uses: actions/setup-python@v5
         with:
-          python-version: "3.x"
+          python-version: ${{ matrix.python-version }}  
+
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
           pip install -r requirements.txt
-          pip install -r test-requirements.txt
+          pip install -r tests/requirements.txt
 
       - name: Run tests
         working-directory: ${{ github.workspace }}
         run: |
           python -m unittest discover -s tests -p 'test_*.py' -t ${{ github.workspace }}
+      
+      - name: Run build test
+        working-directory: ${{ github.workspace }}
+        run: |
+          python -m pip install build
+          python -m build
diff --git a/tests/manual.py b/tests/manual.py
index 9479704..e374f93 100644
--- a/tests/manual.py
+++ b/tests/manual.py
@@ -4,18 +4,16 @@
 from fastapi import FastAPI, Response
 from fastapi.responses import FileResponse
 from pydantic import BaseModel
+from pydom.element import Element
 from seamless import *
-from seamless.element import Element
-from seamless.middlewares.asgi import ASGIMiddleware
+from seamless.extra.transports.socketio.middleware import SocketIOMiddleware
 from seamless.styling import CSS
 from seamless.components import Page as BasePage
 from .server.common import Card, Page, SuperCard, SampleComponent
 from .components import Page as TestPage, App
 
 app = FastAPI()
-app.add_middleware(
-    ASGIMiddleware,
-)
+app.add_middleware(SocketIOMiddleware)
 
 
 def _make_response(response):
diff --git a/test-requirements.txt b/tests/requirements.txt
similarity index 100%
rename from test-requirements.txt
rename to tests/requirements.txt
diff --git a/tests/server/asgi.py b/tests/server/asgi.py
index 9f03d21..f077571 100644
--- a/tests/server/asgi.py
+++ b/tests/server/asgi.py
@@ -3,8 +3,8 @@
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.responses import HTMLResponse, Response
+from seamless.extra.transports.socketio.middleware import SocketIOMiddleware
 
-from seamless.middlewares import ASGIMiddleware
 from .common import index, css_file
 
 HERE = Path(__file__).parent
@@ -19,9 +19,7 @@
     allow_headers=["*"],
 )
 
-app.add_middleware(
-    ASGIMiddleware,
-)
+app.add_middleware(SocketIOMiddleware)
 
 app.get("/c", response_class=HTMLResponse)(index)
 app.get("/static/main.css")(lambda: Response(css_file(), media_type="text/css"))
diff --git a/tests/server/common.py b/tests/server/common.py
index 4cb9c57..c25f3f6 100644
--- a/tests/server/common.py
+++ b/tests/server/common.py
@@ -1,6 +1,7 @@
-from seamless.html import *
 from seamless import Component, render
 from seamless.components import Page as _Page
+from seamless.extra.transports.socketio.transport import SocketIOTransport
+from seamless.html import *
 from seamless.styling import CSS, StyleObject
 
 
@@ -17,6 +18,7 @@ def head(self):
         yield from super().head()
         yield Script(src="/static/main.js", defer=True)
         yield Link(rel="stylesheet", href="/static/main.css")
+        yield SocketIOTransport.init()
 
 
 class AnotherComponent(Component):
diff --git a/tests/test_nested.py b/tests/test_nested.py
index 3c27d20..261ef33 100644
--- a/tests/test_nested.py
+++ b/tests/test_nested.py
@@ -14,5 +14,5 @@ def test_page_inheritance(self):
         self.maxDiff = None
         self.assertEqual(
             render(Page(App())),
-            '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title></title><link rel="stylesheet" href="/static/style.css"></head><body dir="ltr"><div class="card"><h3 class="card-title">Card title</h3><hr><div>Card content</div></div></body></html>',
+            '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="/static/style.css"></head><body dir="ltr"><div class="card"><h3 class="card-title">Card title</h3><hr><div>Card content</div></div></body></html>',
         )
diff --git a/tests/test_rendering.py b/tests/test_rendering.py
index 849d052..a1ea742 100644
--- a/tests/test_rendering.py
+++ b/tests/test_rendering.py
@@ -1,5 +1,5 @@
 from seamless import Component, Div, render
-from seamless.element import Element
+from pydom.element import Element
 
 import unittest