A design system for your everyday projects
Shachi gives you UI building blocks built on the web platform: native Web Components and plain CSS. Import only what you need.
Lightweight.
Accessible.
Yours.
v0.3.1
No framework.
No dependencies.
No lock-in.
Behaviour, semantics and accessibility live in the element. Everything visual is a CSS custom property or a ::part() away.
Components
@shachi-ui/components
Real HTML elements, with the hard parts already done.
Custom elements built on Shadow DOM and web standards. Drop them into plain HTML, React, Vue, Svelte, PHP or a server-rendered app; they don't care what's around them.
Browse all componentsWhat every element gives you, before you write a line of CSS
-
Import only what you ship
Tree-shakable
One entry point per component and no barrel file. Bundlers keep what you use, or let the autoloader fetch elements as they appear.
import "…/button" -
Forms that just work
Form-associated
Controls join native forms through ElementInternals: validation, reset and submission come for free.
new FormData(form) -
Accessible by default
WCAG 2.2 AA
Labels, keyboard support and ARIA state are handled, not left as homework.
role · aria-* · focus -
Speaks your language
i18n
Strings follow the closest lang attribute. English is built in, French is one import away, and yours takes a single call.
<html lang="fr">
Theming
No styles of their own. Your CSS decides.
Components ship structure, behaviour and accessibility, not a look. Every visual decision stays in your stylesheet: start from a ready-made theme, override a single part, or write your own from scratch.
- Same markup in every theme.
- Components document the parts and states they expose.
corehidessh-*elements until defined: no flash of unstyled content.
One <sh-input>, one <sh-switch>, one <sh-button>. Three stylesheets.
same HTML
-
- theme: none
- structure only
-
- theme: brutal
- ready-made theme
-
- theme: yours
- 40 lines of CSS
-
::part()Reach into the shadow root
Each component lists the parts it exposes. Style them like any element.
sh-button::part(button)sh-switch::part(track)sh-dialog::part(panel)
-
:state()Style custom states
Custom states read like
:checked. No class toggling, no attribute soup.sh-switch:state(checked)sh-select:state(open)sh-tab:state(selected)
-
Custom properties
Per-component knobs
One line to change a size, a color or a duration. Each component lists its own.
sh-divider { --thickness }sh-progress { --track-color }sh-expander { --duration }
Styles
@shachi-ui/styles
Plain CSS, organised so you never fight the cascade.
Tokens, layout primitives, element defaults, utilities and a theme, shipped as separate modules. Take all of it, or only the files you need.
Explore the styles
Five layers, one predictable order
@layer shachi.*
-
01
shachi.core
tokens · defaults · document
@layer core -
02
shachi.elements
base HTML styles
@layer elements -
03
shachi.compositions
layout primitives
@layer compositions -
04
shachi.themes
sh-* and ::part()
@layer themes -
05
shachi.utilities
helper classes
@layer utilities wins
↓ later layers win, and your own unlayered CSS beats them all
Layout primitives, not a grid framework: one class each, tuned with custom properties
--sh-gap · --sh-align
-
.stack -
.cluster -
.split -
.flank -
.frame -
.grid
Works with your stack
If it renders HTML, it renders Shachi. No wrappers, no adapters to keep in sync.
Custom elements are part of HTML. Framework-specific setup, SSR and typing are covered in the guides.
Framework guides-
HTML
Autoloader, no build step at all.
<script src=loader.js> -
React
React 19 passes props and events straight through.
<sh-button onClick> -
Vue
Tell the compiler sh-* are custom elements.
isCustomElement -
Svelte
Works out of the box.
bind:value -
PHP
Laravel, Symfony or plain templates: it's just markup.
Blade · Twig -
Astro
Render on the server, upgrade in the browser.
SSR → upgrade -
Turbo
Page visits stay flash-free.
preventTurboFouce() -
Your stack?
If it renders HTML, it works. Recipes for each setup live in the guides.
Framework guides
Quick start
Two ways in. Both take a minute.
Components arrive unstyled. Import the core styles and a theme to dress them.
-
With a bundler
Recommended Vite, webpack… Import each component you use. Nothing else ends up in your bundle.
npm install @shachi-ui/components @shachi-ui/styles import "@shachi-ui/components/button"; import "@shachi-ui/components/switch"; -
No build step
CDN Add the autoloader to any page. It registers each
sh-*element the first time it appears, including ones added later.<script type="module" src="https://unpkg.com/@shachi-ui/components/dist/loader.js"></script> <sh-button variant="primary">Click me</sh-button>
Build with the platform, not around it.
Free and open source under the MIT license.