Skip to content

Per-polygon Interaction

Every polygon rendered by polycss is a real DOM element. You can attach standard event handlers, apply CSS classes, and inspect them in DevTools — that’s true regardless of which entry point you use. The single-polygon primitive is <poly-polygon> (vanilla custom element) / <Poly> (React, Vue).

Loading…

<poly-polygon> (vanilla) and <Poly> (React / Vue) render a single polygon as an atlas-backed <i> for UV-textured and flat-color faces. They forward standard DOM props (onclick, class, style, aria-*, etc.).

<script type="module" src="https://esm.sh/polycss/elements"></script>
<poly-scene>
<!-- Vertices are JSON arrays in the `vertices` attribute. -->
<poly-polygon vertices='[[0,0,0],[1,0,0],[0,1,0]]' color="#ff0000"></poly-polygon>
<poly-polygon
vertices='[[0,0,0],[1,0,0],[0,1,0]]'
color="#0000ff"
texture="/wood.png"
uvs='[[0,0],[1,0],[0,1]]'></poly-polygon>
</poly-scene>
<script type="module" src="https://esm.sh/polycss/elements"></script>
<style>
.highlight { filter: brightness(1.5); }
poly-polygon { transition: filter 0.2s; }
</style>
<poly-scene rot-x="65" rot-y="45" id="scene">
<!-- Polygons are appended programmatically. -->
</poly-scene>
<script type="module">
const scene = document.getElementById("scene");
const polygons = /* Polygon[] from your data source */ [];
for (let i = 0; i < polygons.length; i++) {
const p = polygons[i];
const el = document.createElement("poly-polygon");
el.setAttribute("vertices", JSON.stringify(p.vertices));
if (p.color) el.setAttribute("color", p.color);
el.addEventListener("click", () => alert(`clicked polygon ${i}`));
el.addEventListener("mouseenter", () => el.classList.add("highlight"));
el.addEventListener("mouseleave", () => el.classList.remove("highlight"));
scene.appendChild(el);
}
</script>

Per-polygon override (mesh + custom render)

Section titled “Per-polygon override (mesh + custom render)”

To customize specific polygons inside a loaded mesh, use:

  • Vanilla: load with loadMesh, then manually create <poly-polygon> elements in a loop. You stay in full control of which polygons get special handling.
  • React: the <PolyMesh> render-prop child.
  • Vue: the <PolyMesh> scoped slot.
<script type="module">
import { loadMesh, createPolyScene } from "polycss";
const host = document.getElementById("scene-host");
const scene = createPolyScene(host, { rotX: 65, rotY: 45 });
const result = await loadMesh("/character.glb");
result.polygons.forEach((p, i) => {
const el = document.createElement("poly-polygon");
el.setAttribute("vertices", JSON.stringify(p.vertices));
if (p.color) el.setAttribute("color", p.color);
el.addEventListener("click", () => el.classList.toggle("outlined"));
host.querySelector("poly-scene")?.appendChild(el)
?? host.appendChild(el);
});
</script>

Load a mesh programmatically when you need control over loading state. The vanilla loadMesh is the universal path; React adds a useMesh hook on top that auto-disposes on unmount.

// Vanilla — works in any framework or no framework
import { loadMesh, createPolyScene } from "polycss";
const scene = createPolyScene(document.getElementById("host")!);
const { polygons, dispose } = await loadMesh("/cottage.glb");
scene.add({ polygons, dispose: () => {} });
// ... later:
dispose();
scene.remove();
// React
import { PolyScene, Poly, useMesh } from "@polycss/react";
function Viewer() {
const { polygons, loading, error } = useMesh("/cottage.glb");
if (loading) return <Spinner />;
if (error) return <div>Error: {error}</div>;
return (
<PolyScene>
{polygons.map((p, i) => <Poly key={i} {...p} />)}
</PolyScene>
);
}