HTML components.
Zero runtime.
What if you could have components that were just HTML files without any special syntax? Now you can. Bascik is a build tool for HTML components with automatically scoped CSS and JS. Zero runtime. The code that ships is the code you wrote.
npm create bascik@latest- 0 kB framework JS added
- Zero prod deps (single dev dep)
- 78 pages transpiled in ~1.4s
- Lighthouse 100s out of the box
- Use what you know: vanilla HTML, CSS, JS
How it works
Write your code. Bascik does the rest.
Create a file with your component name. Add your HTML, CSS, and JavaScript. Reference that tag in any page or component with no imports or configuration needed.
<!-- src/pages/index.html -->
<!DOCTYPE html>
<html lang="en">
<body>
<my-card>
<h3>My Card</h3>
<p>Any HTML goes inside as slot content.</p>
</my-card>
</body>
</html> <!-- src/components/my-card.html -->
<style>
.card {
padding: 24px 28px;
border: 1px solid #3a3d40;
border-top: 3px solid #d3ff8d;
border-radius: 10px;
}
</style>
<article class="card">
<div data-bascik-slot></div>
</article> <!-- dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.bascik__my-card__card {
padding: 24px 28px;
border: 1px solid #3a3d40;
border-top: 3px solid #d3ff8d;
border-radius: 10px;
}
</style>
</head>
<body>
<article class="bascik__my-card__card">
<h3>My Card</h3>
<p>Any HTML goes inside as slot content.</p>
</article>
</body>
</html> Live Example
My Card
Any HTML goes inside as slot content.
Build Performance
Sub-second build speeds. Industry-leading performance.
Bascik transpiles and scopes entire static websites in milliseconds. Pure component scoping executes in under 0.2ms per page. This documentation website is a prime real-world example: across 78 pages executing over 500 custom Node.js build scripts to render Markdown and extract code blocks, the entire site transpiles for dev startup in about 1.4 seconds with a warm cache and workers enabled on multi-core hardware.
$ bascik
transpiled: pages/404.html in 0.4ms
transpiled: pages/index.html in 0.8ms
transpiled: pages/cli.html in 0.5ms
transpiled: pages/license.html in 0.3ms
transpiled: pages/getting-started.html in 0.6ms
...
✓ 78 pages transpiled in 1.39s
Server running at http://localhost:8080 Warm vs. Cold Startup Note: The ~1.4s dev startup transpile time above reflects a warm start with workers enabled on multi-core hardware and SHA-256 build script output caching enabled. On a fresh cold start (or after clearing node_modules/.cache/bascik), Bascik runs each uncached build script in its own isolated child process bounded by a concurrency-aware semaphore, compiling all 78 pages and 500+ Markdown scripts in ~3.8s on an M1 MacBook Air. Once cached, subsequent restarts and live-reload edits compile in milliseconds.
Demo
One component. Two isolated instances.
Bascik scopes CSS class names and DOM selectors at build time. Each instance gets its own namespace with no runtime JS, no Shadow DOM, and no virtual DOM.
<!-- src/pages/index.html -->
<!DOCTYPE html>
<html lang="en">
<body>
<demo-counter data-bascik-prop-label="Instance A" />
<demo-counter data-bascik-prop-label="Instance B" />
</body>
</html> <!-- src/components/demo-counter/demo-counter.html -->
<div class="ctr">
<span class="ctr-label" data-bascik-prop-label>
Counter
</span>
<span class="ctr-count" id="count">0</span>
<div class="ctr-btns">
<button class="ctr-dec" id="dec">−</button>
<button class="ctr-inc" id="inc">+</button>
</div>
</div>
<script src="demo-counter.ts"></script> /* src/components/demo-counter/demo-counter.css */
.ctr {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.ctr-count {
font-size: 2.4rem;
font-weight: 700;
color: #d3ff8d;
}
.ctr-dec, .ctr-inc {
width: 40px;
height: 40px;
border-radius: 6px;
cursor: pointer;
} // src/components/demo-counter/demo-counter.ts
const count = document.getElementById('count') as HTMLElement;
const dec = document.getElementById('dec') as HTMLButtonElement;
const inc = document.getElementById('inc') as HTMLButtonElement;
let n: number = 0;
dec.addEventListener('click', () => {
n--;
count.textContent = String(n);
});
inc.addEventListener('click', () => {
n++;
count.textContent = String(n);
}); <!-- dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.bascik__demo-counter__ctr {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.bascik__demo-counter__ctr-count {
font-size: 2.4rem;
font-weight: 700;
color: #d3ff8d;
}
.bascik__demo-counter__ctr-dec,
.bascik__demo-counter__ctr-inc {
width: 40px;
height: 40px;
border-radius: 6px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="bascik__demo-counter__ctr">
<span class="bascik__demo-counter__ctr-label">Instance A</span>
<span class="bascik__demo-counter__ctr-count"
id="bascik__demo-counter__a1b2__count">0</span>
<div class="bascik__demo-counter__ctr-btns">
<button class="bascik__demo-counter__ctr-dec"
id="bascik__demo-counter__a1b2__dec">−</button>
<button class="bascik__demo-counter__ctr-inc"
id="bascik__demo-counter__a1b2__inc">+</button>
</div>
</div>
<script>
(function() {
const count = document.getElementById("bascik__demo-counter__a1b2__count");
const dec = document.getElementById("bascik__demo-counter__a1b2__dec");
const inc = document.getElementById("bascik__demo-counter__a1b2__inc");
let n = 0;
dec.addEventListener("click", () => { n--; count.textContent = String(n); });
inc.addEventListener("click", () => { n++; count.textContent = String(n); });
})();
</script>
</body>
</html> Developer Experience
Built for developer joy.
Component modularity and automatic scoping without the framework friction, complex setups, or runtime overhead.
Works instantly
No bundler setups, complex configs, or transpiler chains. Run bascik and start building instantly with standard Node.js.
Lean in production
Zero production dependencies at build and server runtime. A single optional dependency (chokidar) powers local dev file watching.
Standard HTML & CSS
No JSX, custom template syntax, or framework hooks. Write pure HTML and CSS with standard DOM APIs—everything you know just works.
Automatic isolation
Component styles and DOM queries are scoped automatically at build time. Enjoy total component isolation with zero added JavaScript.
Docs
Explore the docs.
Each feature has a dedicated page with examples and a complete reference.
Core
Components
Build UI with reusable HTML components. No imports or registration needed—just write the custom tag.
CSS
Scoped Styles
Class names, element selectors, @media, and @keyframes are all automatically scoped per component.
JS
Scoped JavaScript
DOM queries in component scripts are rewritten to match scoped IDs and class names at build time.
Slots
Slots
Pass inner HTML into components. Named slots target specific zones with fallback content supported.
Build
Build Scripts
Run arbitrary Node.js at build time with data-bascik-build script blocks. Fetch data, render Markdown, generate anything.
Config
Configuration
Override defaults in bascik.config.ts. Toggle scoping, identifier minification, and code minification per project.
Speed
Lighthouse 100s
Zero runtime dependencies and zero added JS payload for instant load times and perfect scores.
Switching
Switch to Bascik
Moving from React, Next.js, Eleventy, or Astro. Concept mapping and worked examples.
Ready to build?
Up and running in under a minute.
One command scaffolds your project and starts the dev server. No config required to get going.