Skip to content
Getting StartedComponentsStylesClamp Codeberg

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.

npm AI npm i @shachi-ui/components @shachi-ui/styles Set up Shachi UI in this project. Read https://TODO/llms.txt first. Getting Started

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.

Style it your way
Open Shadow DOM Typed with JSDoc WCAG 2.2 AA compliant Form-associated Tree-shakable Theme with plain css i18n built in Works in React, Vue, Svelte, or plain HTML MIT licensed

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 components

What 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.
  • core hides sh-* 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 }
sh-switch::part(track) { border-radius: 999px; } sh-switch:state(checked)::part(track) { background: var(--brand); } sh-expander { --duration: 120ms; }

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.*

  1. shachi.core tokens · defaults · document @layer core
  2. shachi.elements base HTML styles @layer elements
  3. shachi.compositions layout primitives @layer compositions
  4. shachi.themes sh-* and ::part() @layer themes
  5. 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.

  1. 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";
  2. 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>