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
- 50 pages transpiled in ~1.8s
- 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. This documentation website is a prime example: every single one of its 50 pages executes custom Node.js build-time scripts to convert Markdown, extract code blocks, and construct structured search schemas, yet, on a 2020 M1 MacBook Air, the entire site compiles in under 1.8 seconds.
$ bascik
transpiled: pages/404.html
transpiled: pages/index.html
transpiled: pages/search.html
transpiled: pages/license.html
transpiled: pages/getting-started.html
...
✓ 50 pages transpiled in 1779ms
Server running at http://localhost:8080Demo
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.
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.