Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
4a6aba8
chore: add examples/* to pnpm workspace
apresmoi May 21, 2026
6e50e8d
feat(examples): add html custom-elements examples app
apresmoi May 21, 2026
906f24d
feat(examples): add vanilla imperative examples app
apresmoi May 21, 2026
895c734
feat(examples): add React examples app
apresmoi May 21, 2026
a6e4a63
feat(examples): add Vue examples app
apresmoi May 21, 2026
5549bb8
fix(polycss): set crossOrigin on texture image loader for cross-origi…
apresmoi May 21, 2026
414d6cc
fix(examples-html): size poly-scene so .polycss-scene centers correctly
apresmoi May 21, 2026
1fc7062
fix(polycss): default .polycss-camera to fill parent so the scene cen…
apresmoi May 21, 2026
8d9d6a4
fix(examples): set zoom: 0.3 on all example cameras
apresmoi May 21, 2026
805648e
fix(examples): zoom 0.1 on all example cameras
apresmoi May 21, 2026
2d63895
feat: add .polycss-camera wrapper in createPolyScene and move perspec…
apresmoi May 21, 2026
3d44fb6
fix: retarget polycss-fpv-host class to .polycss-camera wrapper in FP…
apresmoi May 21, 2026
f9ab8b8
test: update perspective assertions to target .polycss-camera wrapper
apresmoi May 21, 2026
3af96c7
fix: withMeshResolution always returns optimized polygons, not origin…
apresmoi May 21, 2026
252fd48
fix(core): apply final mergePolygons pass after optimizeMeshPolygons …
apresmoi May 21, 2026
838d183
feat(polycss): export atlas planning helpers for framework packages
apresmoi May 21, 2026
4859bd5
refactor(react): migrate scene/textureAtlas to import pure logic from…
apresmoi May 21, 2026
76a65c9
refactor(vue): migrate scene/textureAtlas to import pure logic from p…
apresmoi May 21, 2026
c496484
chore: add @layoutit/polycss as workspace dep in react and vue packages
apresmoi May 21, 2026
3bf1778
Merge remote-tracking branch 'origin/main' into feat/examples
apresmoi May 21, 2026
18f3908
fix(react,vue): dispatch projective quad plans to TextureProjectiveSo…
apresmoi May 21, 2026
4afe8d4
fix(react,vue): emit projectiveMatrix verbatim (avoid 3-decimal re-ro…
apresmoi May 21, 2026
9278f30
fix(react,vue): drop double matrix3d wrap in TextureBorderShapePoly
apresmoi May 21, 2026
b6c61f0
fix(vue): split background shorthand vs longhand by lighting mode to …
apresmoi May 21, 2026
faad299
Merge remote-tracking branch 'origin/main' into feat/examples
apresmoi May 21, 2026
77cac02
fix(react,vue): SOLID_TRIANGLE_CANONICAL_SIZE 64 → 32 to match canoni…
apresmoi May 21, 2026
7bd7f32
test(polycss): feature tests for atlas plan computation
apresmoi May 21, 2026
b2bc855
test(polycss): feature tests for strategy selection and filter
apresmoi May 21, 2026
b25ad7b
test(polycss): feature tests for matrix and CSS length formatting
apresmoi May 21, 2026
b545287
test(polycss): feature tests for border-shape geometry and matrix
apresmoi May 21, 2026
23b7e18
test(polycss): feature tests for atlas packing and edge repair
apresmoi May 21, 2026
f5cd831
test(polycss): feature tests for solid paint defaults computation
apresmoi May 21, 2026
ec2b929
test(polycss): feature tests for solid triangle primitive dispatch
apresmoi May 21, 2026
178f6f9
test(polycss): feature tests for stable triangle DOM operations
apresmoi May 21, 2026
80deb24
refactor(polycss): move constants to render/atlas/constants.ts
apresmoi May 21, 2026
ee2cb6d
refactor(polycss): move types to render/atlas/types.ts
apresmoi May 21, 2026
d80b9b2
refactor(polycss): move matrix formatting to render/atlas/matrix.ts
apresmoi May 21, 2026
d7d30f9
refactor(polycss): move solid triangle geometry to render/atlas/solid…
apresmoi May 21, 2026
2fb19f1
refactor(polycss): move paint defaults to render/atlas/paintDefaults.ts
apresmoi May 21, 2026
9c73f7d
refactor(polycss): move atlas plan computation to render/atlas/plan.ts
apresmoi May 21, 2026
19c43fe
refactor(polycss): move render strategy selection to render/atlas/str…
apresmoi May 21, 2026
dc92491
refactor(polycss): move border-shape geometry to render/atlas/borderS…
apresmoi May 21, 2026
542f9ca
refactor(polycss): move texture edge repair to render/atlas/edgeRepai…
apresmoi May 21, 2026
b4710da
refactor(polycss): move atlas packing to render/atlas/packing.ts
apresmoi May 21, 2026
edabb62
refactor(polycss): move atlas rasterisation to render/atlas/rasterise.ts
apresmoi May 21, 2026
fe50a33
refactor(polycss): move DOM element emission to render/atlas/emit.ts
apresmoi May 21, 2026
881cd62
refactor(polycss): move solid triangle plan to render/atlas/solidTria…
apresmoi May 21, 2026
5e8a966
refactor(polycss): move stable triangle renderer to render/atlas/stab…
apresmoi May 21, 2026
9208b89
refactor(polycss): move top-level render entry points to render/atlas…
apresmoi May 21, 2026
d7ce44c
refactor(polycss): wire atlas/index.ts barrel and reduce textureAtlas…
apresmoi May 21, 2026
3e81086
refactor(core): move atlas constants from polycss to core/atlas/const…
apresmoi May 21, 2026
6794b9c
refactor(core): move atlas matrix formatting from polycss to core/atl…
apresmoi May 21, 2026
e87839e
refactor(core): move buildTextureEdgeRepairSets from polycss to core/…
apresmoi May 21, 2026
9363c38
refactor(core): extract pure atlas types from polycss to core/atlas/t…
apresmoi May 21, 2026
9527302
refactor(core): extract pure paint math from polycss to core/atlas/pa…
apresmoi May 21, 2026
7c517c6
refactor(core): extract pure atlas strategy predicates from polycss t…
apresmoi May 21, 2026
95d2229
refactor(core): extract pure solidTriangle and borderShape geometry f…
apresmoi May 21, 2026
6a5c28c
refactor(core): extract pure solidTrianglePlan math from polycss to c…
apresmoi May 22, 2026
1d4e5d9
refactor(core): extract pure plan math from polycss to core/atlas/pla…
apresmoi May 22, 2026
4c3bcc5
refactor(core): extract pure packing math from polycss to core/atlas/…
apresmoi May 22, 2026
90a42b4
feat(react): add local atlas browser helpers
apresmoi May 22, 2026
213a09e
refactor(react): import atlas browser helpers locally, drop polycss dep
apresmoi May 22, 2026
4b012b6
chore(react): drop @layoutit/polycss dependency
apresmoi May 22, 2026
ee61d2e
feat(vue): add local atlas browser helpers
apresmoi May 22, 2026
bb7365f
refactor(vue): import atlas browser helpers locally, drop polycss dep
apresmoi May 22, 2026
04be876
chore(vue): drop @layoutit/polycss dependency
apresmoi May 22, 2026
a5fd369
test(react): add atlasBrowser detection tests
apresmoi May 22, 2026
0d846a3
test(react): add atlasBrowser filterAtlasPlans tests
apresmoi May 22, 2026
c6423ce
test(react): add atlasBrowser packing tests
apresmoi May 22, 2026
9e76a36
test(react): add atlasBrowser paintDefaults tests
apresmoi May 22, 2026
eca2928
test(react): add atlasBrowser buildAtlasPages smoke tests
apresmoi May 22, 2026
96ba85e
test(vue): add atlasBrowser detection tests
apresmoi May 22, 2026
96ffa70
test(vue): add atlasBrowser filterAtlasPlans tests
apresmoi May 22, 2026
39b0958
test(vue): add atlasBrowser packing tests
apresmoi May 22, 2026
72f619e
test(vue): add atlasBrowser paintDefaults tests
apresmoi May 22, 2026
ec92277
test(vue): add atlasBrowser buildAtlasPages smoke tests
apresmoi May 22, 2026
36fa056
chore(polycss): delete edgeRepair.ts re-export stub, import from core…
apresmoi May 22, 2026
566a5ed
chore(polycss): delete constants.ts re-export stub, import from core …
apresmoi May 22, 2026
91b24f5
chore(polycss): delete matrix.ts re-export stub, import from core dir…
apresmoi May 22, 2026
4f9abba
chore(polycss): delete solidTriangle.ts re-export stub, import from c…
apresmoi May 22, 2026
a2493ee
chore(polycss): delete borderShape.ts re-export stub, import from cor…
apresmoi May 22, 2026
2680c02
chore(polycss): remove dead core re-exports from packing.ts, import f…
apresmoi May 22, 2026
cdff544
chore(polycss): remove dead core re-exports from plan.ts, import from…
apresmoi May 22, 2026
19b69d6
test: co-locate render/__tests__ files next to their source modules
apresmoi May 22, 2026
c116c99
chore(polycss): remove dead core re-exports from solidTrianglePlan.ts…
apresmoi May 22, 2026
0ccf9df
chore(polycss): remove dead core re-exports from paintDefaults.ts, im…
apresmoi May 22, 2026
5644447
chore(polycss): drop unused SolidTrianglePlan import from emit.ts
apresmoi May 22, 2026
5860963
chore(polycss): remove dead core type re-exports from types.ts, impor…
apresmoi May 22, 2026
6a8e646
test(core): feature tests for atlas/edgeRepair
apresmoi May 22, 2026
b720789
test(core): feature tests for atlas/packing
apresmoi May 22, 2026
76eab87
test(core): feature tests for atlas/strategy
apresmoi May 22, 2026
01363a1
test(core): feature tests for atlas/paintDefaults
apresmoi May 22, 2026
9d7b1a2
test(core): feature tests for atlas/solidTriangle
apresmoi May 22, 2026
63ccd35
test(core): extend atlas/borderShape tests for edge cases and new exp…
apresmoi May 22, 2026
e4ac37f
test(core): extend atlas/matrix tests for formatter functions
apresmoi May 22, 2026
d0ffd1c
test(core): extend atlas/plan tests for UV, basis, and guard resolution
apresmoi May 22, 2026
2dc4889
test(core): extend atlas/solidTriangle tests for offset functions
apresmoi May 22, 2026
243ffe3
refactor(react): split textureAtlas.tsx + atlasBrowser.ts into scene/…
apresmoi May 22, 2026
3d6b8fd
refactor(vue): split textureAtlas.ts + atlasBrowser.ts into scene/atlas/
apresmoi May 22, 2026
d714157
refactor(react): remove textureAtlas/atlasBrowser barrels, import fro…
apresmoi May 22, 2026
ed0cbfe
refactor(vue): remove textureAtlas/atlasBrowser barrels, import from …
apresmoi May 22, 2026
3001277
chore(examples): add orbit-controls animate to all examples for flick…
apresmoi May 22, 2026
9ffc44c
fix(react,vue): replace PolyScene sceneStyle transform with useLayout…
apresmoi May 22, 2026
a3a7a77
chore(react): add per-second render counter instrumentation for flick…
apresmoi May 22, 2026
066a187
fix(react): memo-wrap leaf polygon components to prevent stale-transf…
apresmoi May 22, 2026
93298cb
chore(react): remove render-count instrumentation added for flicker d…
apresmoi May 22, 2026
1efccec
fix(react,vue): add will-change: transform to .polycss-scene to preve…
apresmoi May 22, 2026
eb29ef3
docs(agents): update for renderer-owned browser glue + examples works…
apresmoi May 22, 2026
ee1c3e4
chore: remove stale __tests__ files and refresh lockfile
apresmoi May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ Monorepo layout (pnpm workspaces):

| Package | npm name | Role |
|---|---|---|
| `packages/core` | `@layoutit/polycss-core` | Pure math: Vec3, Polygon, scene, camera, mesh ops, atlas planning. Zero browser globals. |
| `packages/polycss` | `@layoutit/polycss` | Vanilla renderer + custom elements (`<poly-scene>`, etc.). Owns DOM emission, CSS injection, atlas rasterisation. |
| `packages/react` | `@layoutit/polycss-react` | React components + hooks. Thin wrapper over core + polycss. |
| `packages/vue` | `@layoutit/polycss-vue` | Vue 3 mirror of the React package. |
| `packages/core` | `@layoutit/polycss-core` | Pure math: Vec3, Polygon, scene, camera, mesh ops, atlas planning. Zero browser globals (lib: ES2020 only). |
| `packages/polycss` | `@layoutit/polycss` | Vanilla renderer + custom elements (`<poly-scene>`, etc.). Owns DOM emission, CSS injection, its own copy of atlas rasterisation. Depends on `core` only. |
| `packages/react` | `@layoutit/polycss-react` | React components + hooks. Owns its own copy of atlas rasterisation. Depends on `core` only — **NOT on `polycss`.** |
| `packages/vue` | `@layoutit/polycss-vue` | Vue 3 mirror of the React package. Owns its own copy of atlas rasterisation. Depends on `core` only. |
| `website` | `@layoutit/polycss-website` | Astro + Starlight docs site. Not published. |
| `examples/{html,vanilla,react,vue}` | private | Per-framework Vite apps demonstrating the minimal usage for each renderer. Workspace members so they resolve to local `workspace:^` packages. Not published. |

Public API is **mirrored** across React and Vue. Adding a hook on one side without adding the matching composable on the other is not acceptable (see "Cross-package discipline" below).

Expand Down Expand Up @@ -95,13 +96,16 @@ The React and Vue packages are mirror images. **Any public API change in one mus

When you change `packages/polycss` or `packages/core` in a way that affects the public surface (new option, renamed export, changed default), the React and Vue bindings update in the same PR. Don't ship a polycss change that leaves the bindings stale.

**Renderer-owned browser glue.** The canvas atlas pipeline (`buildAtlasPages` + helpers), browser-feature detection (`isBorderShapeSupported`, `isSolidTriangleSupported`, `resolveSolidTrianglePrimitive`), and the injected `.polycss-scene` / `.polycss-camera` base styles exist as **independent copies** in three places: `packages/polycss/src/render/atlas/`, `packages/react/src/scene/atlas/`, `packages/vue/src/scene/atlas/` (plus three sibling `styles.ts` files). This is deliberate — each renderer is self-contained on its dep graph (React/Vue do not import from polycss). The trade-off is that a bug fix in any of these files MUST be mirrored into the other two. Coverage is pinned per copy by the co-located test files.

Before opening a PR:

- [ ] If I touched a React component/hook, the Vue composable/component matches.
- [ ] If I touched a Vue component/composable, the React component/hook matches.
- [ ] If I added an option to a `polycss` factory, both bindings expose it.
- [ ] If I renamed a `core` export, every package that imports it is updated.
- [ ] If I touched the renderer, `packages/polycss/src/styles/styles.ts` is consistent with the new behavior (CSS rules cover every emitted tag for both lighting modes).
- [ ] If I touched the canvas atlas pipeline (`rasterise.ts` / `buildAtlasPages.ts`) or browser-feature detection in ONE renderer, the same fix lands in the other two renderers (polycss + react + vue) in this PR.
- [ ] If I touched any of the three `styles.ts` (`packages/polycss/src/styles/styles.ts`, `packages/react/src/styles/styles.ts`, `packages/vue/src/styles/styles.ts`), the other two are consistent — CSS rules cover every emitted tag for both lighting modes, and shared properties like `will-change: transform` on `.polycss-scene` exist in all three.
- [ ] Website docs (`website/src/content/docs/**`) and READMEs reflect any user-visible change.
- [ ] If I changed a render strategy, lighting mode, naming convention, or the JS-in-render-loop rules, `AGENTS.md` reflects the new state in this same PR.

Expand Down
231 changes: 231 additions & 0 deletions bench/flicker-diagnose.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
#!/usr/bin/env node
/**
* Flicker diagnostic: records every style/attribute/childList mutation on
* polycss elements over ~3 seconds of orbit animation, then prints a summary
* grouped by element + attribute showing which elements mutate most.
*
* Also reports key CSS properties (will-change, transformStyle) on the scene
* element — missing `will-change: transform` on .polycss-scene is the known
* root cause of baked-shapes flicker in React/Vue (the scene re-rasterizes
* every leaf on each frame instead of compositing a cached GPU layer).
*
* Usage:
* node bench/flicker-diagnose.mjs --port=5175
* node bench/flicker-diagnose.mjs --port=5173 # html baseline
* node bench/flicker-diagnose.mjs --port=5174 # vanilla baseline
* node bench/flicker-diagnose.mjs --port=5176 # vue
*
* Output: mutation counts/sec per element, top mutated attributes, CSS
* property snapshot, and a sample of value transitions for high-frequency
* mutated elements.
*/
import { chromium } from "playwright";
import { chromiumArgsWithGpuDefault } from "./chromium-defaults.mjs";

const argv = process.argv.slice(2);
const optStr = (name, dflt = "") => {
const i = argv.indexOf(`--${name}`);
if (i >= 0) return argv[i + 1] ?? dflt;
const eq = argv.find((a) => a.startsWith(`--${name}=`));
return eq ? eq.slice(name.length + 3) : dflt;
};
const hasFlag = (name) => argv.includes(`--${name}`) || argv.includes(`--${name}=true`);

const PORT = optStr("port", "5175");
const OBSERVE_MS = 3000;
const WARMUP_MS = 2000;
const HEADED = hasFlag("headed");
const TOP_N = 10;

const url = `http://localhost:${PORT}/baked-shapes/`;
console.log(`[flicker-diagnose] target: ${url}`);
console.log(`[flicker-diagnose] warmup: ${WARMUP_MS}ms observe: ${OBSERVE_MS}ms`);

const browser = await chromium.launch({
headless: !HEADED,
args: chromiumArgsWithGpuDefault([], { softwareBackend: false }),
});

try {
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await ctx.newPage();

await page.goto(url, { waitUntil: "networkidle", timeout: 30000 });

// Wait for mesh children to appear (up to 15s)
await page.waitForFunction(
() => {
const mesh = document.querySelector(".polycss-mesh");
return mesh && mesh.children.length > 0;
},
null,
{ timeout: 15000 },
);

// Let orbit animation run for warmup before we start recording
await page.waitForTimeout(WARMUP_MS);

// Snapshot key CSS properties before observing
const cssSnapshot = await page.evaluate(() => {
const scene = document.querySelector(".polycss-scene");
if (!scene) return null;
const cs = window.getComputedStyle(scene);
return {
willChange: cs.willChange,
transformStyle: cs.transformStyle,
perspective: cs.perspective,
};
});

// Inject MutationObserver and collect records
const records = await page.evaluate(
({ observeMs }) =>
new Promise((resolve) => {
const log = [];
const t0 = performance.now();

const observer = new MutationObserver((mutations) => {
const ts = performance.now() - t0;
for (const m of mutations) {
const el = m.target;
const tag = el.tagName?.toLowerCase() ?? "?";
const cls = el.className ? String(el.className).trim().split(/\s+/).join(".") : "";
const id = `${tag}${cls ? "." + cls : ""}`;
if (m.type === "attributes") {
log.push({
ts,
type: "attr",
id,
attr: m.attributeName ?? "",
oldValue: m.oldValue ?? null,
newValue: el.getAttribute(m.attributeName ?? "") ?? null,
});
} else if (m.type === "childList") {
log.push({
ts,
type: "childList",
id,
added: m.addedNodes.length,
removed: m.removedNodes.length,
});
}
}
});

// Observe from document.body to catch all mutations including the scene element
observer.observe(document.body, {
subtree: true,
childList: true,
attributes: true,
attributeOldValue: true,
attributeFilter: ["style", "class", "transform"],
});

setTimeout(() => {
observer.disconnect();
resolve(log);
}, observeMs);
}),
{ observeMs: OBSERVE_MS },
);

await ctx.close();

// ── CSS snapshot ──────────────────────────────────────────────────────────
console.log("\n[flicker-diagnose] .polycss-scene CSS snapshot:");
if (cssSnapshot) {
const wc = cssSnapshot.willChange;
const ok = wc === "transform";
console.log(` will-change: ${wc} ${ok ? "(OK — GPU layer promoted)" : "(MISSING — re-rasterizes every frame; FLICKER SOURCE)"}`);
console.log(` transform-style: ${cssSnapshot.transformStyle}`);
console.log(` perspective: ${cssSnapshot.perspective}`);
} else {
console.log(" (scene element not found)");
}

// ── Analysis ──────────────────────────────────────────────────────────────

const totalMs = OBSERVE_MS;
const totalMutations = records.length;
const mutPerSec = (totalMutations / (totalMs / 1000)).toFixed(1);

// Filter out the orbit animation's per-frame scene transform update — that's
// expected (1 write/frame) and is NOT the flicker source.
const SCENE_STYLE_ORBIT_PATTERN = /scale\(.+?\) rotateX/;
const interestingRecords = records.filter((r) => {
if (r.type === "attr" && r.attr === "style" && r.id.includes("polycss-scene")) {
// Only flag if the new value does NOT look like a normal orbit transform
return !SCENE_STYLE_ORBIT_PATTERN.test(r.newValue ?? "");
}
return true;
});

console.log(`\n[flicker-diagnose] port=${PORT} total mutations: ${totalMutations} (${mutPerSec}/sec)`);
console.log(` (orbit transform updates: ${totalMutations - interestingRecords.length}, interesting: ${interestingRecords.length})\n`);

// Group by element id + attribute
const byKey = new Map();
for (const rec of records) {
if (rec.type === "attr") {
const key = `${rec.id} [${rec.attr}]`;
if (!byKey.has(key)) byKey.set(key, { count: 0, values: new Map(), transitions: [] });
const entry = byKey.get(key);
entry.count++;
const vNew = rec.newValue ?? "(null)";
const vOld = rec.oldValue ?? "(null)";
entry.values.set(vNew, (entry.values.get(vNew) ?? 0) + 1);
if (entry.transitions.length < 5) entry.transitions.push({ from: vOld, to: vNew });
} else {
const key = `${rec.id} [childList +${rec.added}/-${rec.removed}]`;
if (!byKey.has(key)) byKey.set(key, { count: 0, values: new Map(), transitions: [] });
byKey.get(key).count++;
}
}

const sorted = [...byKey.entries()].sort((a, b) => b[1].count - a[1].count);

if (sorted.length === 0) {
console.log(" No mutations recorded.\n");
} else {
console.log(` Top ${Math.min(TOP_N, sorted.length)} most-mutated element+attribute combos:`);
console.log(" " + "─".repeat(80));
for (const [key, data] of sorted.slice(0, TOP_N)) {
const rate = (data.count / (totalMs / 1000)).toFixed(1);
console.log(` ${key}`);
console.log(` mutations: ${data.count} (${rate}/sec)`);
const uniqueVals = [...data.values.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
if (uniqueVals.length > 0) {
const preview = uniqueVals
.map(([v, n]) => `"${v.length > 60 ? v.slice(0, 57) + "..." : v}" (×${n})`)
.join(", ");
console.log(` unique values: ${uniqueVals.length} top: ${preview}`);
}
if (data.transitions.length > 0) {
console.log(" sample transitions:");
for (const t of data.transitions.slice(0, 2)) {
const f = t.from?.length > 60 ? t.from.slice(0, 57) + "..." : t.from;
const to = t.to?.length > 60 ? t.to.slice(0, 57) + "..." : t.to;
console.log(` ${f} → ${to}`);
}
}
console.log();
}
}

// Group by just element tag to see which tags flicker most
const byTag = new Map();
for (const rec of records) {
const tag = rec.id.split("[")[0].trim();
byTag.set(tag, (byTag.get(tag) ?? 0) + 1);
}
const tagsSorted = [...byTag.entries()].sort((a, b) => b[1] - a[1]);
console.log(" Mutations by element tag:");
for (const [tag, count] of tagsSorted) {
const rate = (count / (totalMs / 1000)).toFixed(1);
console.log(` ${tag.padEnd(40)} ${count.toString().padStart(6)} mutations (${rate}/sec)`);
}

console.log();
} finally {
await browser.close();
}
21 changes: 21 additions & 0 deletions examples/html/baked-shapes/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>baked-shapes — polycss HTML</title>
<script type="module">import "@layoutit/polycss/elements";</script>
<style>
html, body { margin: 0; height: 100%; background: #111; } poly-scene { display: block; width: 100%; height: 100%; }
poly-camera { display: block; width: 100%; height: 100vh; }
</style>
</head>
<body>
<poly-camera rot-x="65" rot-y="45" zoom="0.1">
<poly-scene>
<poly-orbit-controls animate-speed="0.3"></poly-orbit-controls>
<poly-icosahedron size="100" color="#ff6644"></poly-icosahedron>
</poly-scene>
</poly-camera>
</body>
</html>
26 changes: 26 additions & 0 deletions examples/html/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>polycss — HTML examples</title>
<style>
body { background: #111; color: #eee; font-family: sans-serif; padding: 2rem; }
h1 { margin-bottom: 1rem; }
ul { list-style: none; padding: 0; }
li { margin: 0.5rem 0; }
a { color: #7dd3fc; text-decoration: none; font-size: 1.1rem; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>polycss — HTML (custom elements)</h1>
<ul>
<li><a href="baked-shapes/">baked-shapes</a></li>
<li><a href="multi-mesh/">multi-mesh</a></li>
<li><a href="solid-glb/">solid-glb</a></li>
<li><a href="textured-glb/">textured-glb</a></li>
<li><a href="voxel/">voxel</a></li>
</ul>
</body>
</html>
23 changes: 23 additions & 0 deletions examples/html/multi-mesh/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>multi-mesh — polycss HTML</title>
<script type="module">import "@layoutit/polycss/elements";</script>
<style>
html, body { margin: 0; height: 100%; background: #111; } poly-scene { display: block; width: 100%; height: 100%; }
poly-camera { display: block; width: 100%; height: 100vh; }
</style>
</head>
<body>
<poly-camera rot-x="65" rot-y="45" zoom="0.1">
<poly-scene>
<poly-orbit-controls animate-speed="0.3"></poly-orbit-controls>
<poly-box size="80" color="#4ecdc4" position="-120,0,0"></poly-box>
<poly-torus color="#ff6644" position="0,0,0"></poly-torus>
<poly-cone color="#ffd166" position="120,0,0"></poly-cone>
</poly-scene>
</poly-camera>
</body>
</html>
18 changes: 18 additions & 0 deletions examples/html/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@layoutit/polycss-examples-html",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@layoutit/polycss": "workspace:^"
},
"devDependencies": {
"typescript": "^5.3.3",
"vite": "^6.0.0"
}
}
21 changes: 21 additions & 0 deletions examples/html/solid-glb/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>solid-glb — polycss HTML</title>
<script type="module">import "@layoutit/polycss/elements";</script>
<style>
html, body { margin: 0; height: 100%; background: #111; } poly-scene { display: block; width: 100%; height: 100%; }
poly-perspective-camera { display: block; width: 100%; height: 100vh; }
</style>
</head>
<body>
<poly-perspective-camera rot-x="65" rot-y="45" zoom="0.1">
<poly-scene>
<poly-orbit-controls animate-speed="0.3"></poly-orbit-controls>
<poly-mesh src="https://polycss.com/gallery/glb/apple.glb" auto-center></poly-mesh>
</poly-scene>
</poly-perspective-camera>
</body>
</html>
Loading
Loading