GridKit Web Components provides design-system elements that work in any web application. They are native custom elements built with Lit, so they can be used in plain HTML, Angular, Vue, React, or another framework.
Available elements: gd-avatar, gd-badge, gd-box, gd-button, gd-checkbox, gd-icon, gd-image, gd-input, gd-input-file, gd-label, gd-link, gd-loader, gd-select, gd-separator, gd-skeleton, gd-slider, gd-slider-dots, gd-switch, gd-textarea, gd-toggle, gd-truncate, gd-typography, gd-wrapper, gd-counter, gd-menu, gd-chat-container, gd-column, gd-flex-container, gd-row, and gd-scroll.
npm install web-componentsweb-components is standalone: the GridKit design tokens and icons are bundled in, so you do not install gd-design-library. Import the package once to register its custom elements. Each element needs a GridKit theme; assign it as a JavaScript property, not as an HTML attribute.
import { defaultTheme } from 'web-components'; // also registers every gd-* custom element
document.querySelectorAll<HTMLElement & { theme: unknown }>('gd-button, gd-checkbox, gd-input').forEach((element) => {
element.theme = defaultTheme;
});<gd-button variant="primary">Save</gd-button>
<gd-checkbox>Accept terms</gd-checkbox>
<gd-input label="Email" placeholder="name@example.com"></gd-input>
<gd-typography variant="h1" as="h1">Welcome</gd-typography>Use attributes for strings and booleans, such as variant, label, and disabled. Pass objects, arrays, and controlled values as JavaScript properties. Components emit native custom events: gd-input returns { value }, while gd-change returns the selected value or checkbox state.
<gd-theme-provider> is the counterpart of React's ThemeProvider + useTheme(). Every gd-* element
below it — including inside other elements' shadow roots — uses its theme and re-renders when it changes.
The bundled tokens are the same ones gd-design-library ships for React, so one theme object shape drives both.
<gd-theme-provider is-default>
<gd-button>Save</gd-button>
</gd-theme-provider>const provider = document.querySelector('gd-theme-provider')!;
provider.addTheme('dark', darkTheme); // like addTheme() from useTheme(); skips an existing name
provider.setTheme('dark'); // like setTheme(); unknown names revert to the initial theme
provider.setAttribute('theme-name', 'dark'); // declarative equivalent
provider.addEventListener('gd-theme-change', (e) => console.log(e.detail.name));is-default(or noinitialTheme) starts fromdefaultTheme; otherwise setprovider.initialTheme, whosenameregisters it.- A theme assigned directly (
el.theme = x) always wins over the provider for that element. - Without a provider,
themestays{}as before. Nested providers: the nearest one wins. - Custom elements that extend
GdThemedElementget the same behavior.
Register the elements once (for example, in main.ts), then allow custom elements in the consuming component or module.
// main.ts
import 'web-components';
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { defaultTheme } from 'web-components';
@Component({
selector: 'app-root',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<gd-input [theme]="theme" label="Email" [value]="email" (gd-input)="email = $event.detail.value"></gd-input>
<gd-checkbox [theme]="theme" [checked]="accepted" (gd-change)="accepted = $event.detail.checked">
Accept terms
</gd-checkbox>
<gd-button [theme]="theme" variant="primary" (click)="save()">Save</gd-button>
`,
})
export class AppComponent {
theme = defaultTheme;
email = '';
accepted = false;
save() {}
}Use Angular property bindings ([theme], [items], [value]) for non-string values and event bindings ((gd-input), (gd-change)) for component events.
Register the elements once in your app entry file. In templates, use .prop for object values and component state.
// main.ts
import { createApp } from 'vue';
import 'web-components';
import App from './App.vue';
createApp(App).mount('#app');<script setup lang="ts">
import { ref } from 'vue';
import { defaultTheme } from 'web-components';
const email = ref('');
const accepted = ref(false);
</script>
<template>
<gd-input :theme.prop="defaultTheme" label="Email" :value.prop="email" @gd-input="email = $event.detail.value" />
<gd-checkbox :theme.prop="defaultTheme" :checked.prop="accepted" @gd-change="accepted = $event.detail.checked">
Accept terms
</gd-checkbox>
<gd-button :theme.prop="defaultTheme" variant="primary">Save</gd-button>
</template>The shared Storybook is started from the repository root with npm run storybook and
opened at http://localhost:6006. Its Web Components section contains native stories, Controls, and docs for all twenty-five
existing atoms and molecules plus the separately exported Column, FlexContainer, Portal, Row, and Scroll components. Interactive stories show native event payloads below the component.
| Storybook component | React-contract coverage |
|---|---|
| Avatar | Image, fallback, badge, every size, custom colors |
| Badge | Variants, appearances, sizes, disabled state, and icon slots |
| Box | Vertical/horizontal layout, borders, highlight, shadow hover, and slotted content |
| Button | Every variant and radius, full-width, icon-only, loading, disabled, icon slots |
| Checkbox | Checked, indeterminate, disabled, both sizes; gd-change → { checked } |
| Input | Types, validation colors, read-only/disabled, adornments; gd-input/gd-change |
| Image | Loading placeholder, fallback slot, caption, sizing, object fit, and load/error events |
| Icon | All built-in icons from the shared core catalog, token sizes, and theme-aware fills |
| InputFile | Accept/capture/multiple/disabled behavior, custom label, icon label, and gd-change file detail |
| Label | Native label association, slotted content, icons, and style overrides |
| Link component | Variants, sizes, underline states, disabled semantics, targets, and href |
| Loader | Circle/dots, sizes, rounded dots, wrappers, section, and native full-page top layer |
| Select | Single/multiple, search, auto-open, colors, adornments, custom initiator; gd-change |
| Separator | Orientations, line variants, thickness, labels, semantic elements, lengths, and colors |
| Skeleton | Rounded/rectangular/circular shapes, dimensions, colors, animation control, and child content |
| Slider | Range limits, controlled values, keyboard input, disabled state, visual fill, and events |
| SliderDots | Accessible carousel tabs, active state, larger sets, and selection events |
| Switch | Checked, disabled, loading, controlled/uncontrolled behavior, label placement, and events |
| Textarea | Values, colors, resize modes, dynamic height, focus, character limits, and input/change events |
| Toggle | String/object items, selected value, disabled state, custom item rendering, and change events |
| Truncate | Single/multiple-line truncation, style overrides, overflow measurement, and accessibility |
| Typography | All 18 variants, display sizes, style variants, alignment/color controls, semantic override |
| Wrapper | Inline, section and full-page layout variants, semantic tag override, content, and styles |
| Counter | Minimum/maximum, custom range, disabled; gd-change → { value: number } |
| Menu | Placement, offsets, height constraints, close/persist behavior; gd-change → { data, value } |
| ChatContainer | Responsive named-slot layout, collapsed rail, gd-toggle-sidebar, and imperative state methods |
| Column | gd-column; native flex-element and Box style surface, styles, and slotted children |
| FlexContainer | gd-flex-container; complete Box prop surface, styles, classes, and slotted children |
| Portal | gd-portal; target placement, wrapper variants/tags, styles, cleanup, and scroll blocking |
| Row | gd-row; native flex-element and Box style surface, styles, and slotted children |
| Scroll | Vertical/horizontal modes, automatic overflow, auto-hide, draggable thumbs, and imperative scrolling |
Each React layout export has a matching registered custom element, module, public class, Storybook entry, and parity contract. ChatContainer maps its five React content regions to the default, header, sidebar, sidebar-header, and sidebar-minified slots; emits gd-toggle-sidebar; and exposes isOpen, open(), close(), and toggle() directly on the element. Column and Row preserve as, align, justify, gutter, isWrap, isReversed, flex, styles, and every browser-relevant BoxCssComponentProps style property. As with Typography, as changes the semantic element carrying part="root" inside the shadow tree while the public gd-column or gd-row host remains stable. FlexContainer preserves the complete Box style surface, styles, and children; consumers use the native class attribute rather than a duplicated React className API. Portal preserves container, wrapperVariant, withWrapper, blocksScroll, WrapperView, styles, semantic wrapper output, and child node identity.
<gd-row align="center" justify="between" gutter="8" is-reversed>
<article>First</article>
<article>Second</article>
</gd-row>Object values and boolean false values use JavaScript properties: column.isWrap = false and column.styles = { padding: '10px' }.
gd-portal moves its original light-DOM nodes into document.body or a supplied Element/DocumentFragment, rather than cloning them, so native node identity and listeners are retained. When a wrapper is enabled, portal.portalRoot exposes the generated semantic element. As with every framework-neutral custom element, React-specific context and synthetic-event semantics remain the responsibility of a React adapter; the native contract uses standard DOM ownership and events.
The ports preserve each source component's component-specific public contract using native
custom-element equivalents. Primitive props remain attributes/properties; objects, arrays,
functions, themes, and style objects are JavaScript properties. React callbacks become native
DOM/custom events, ReactNode props become default or named slots, and imperative refs become
public element methods. Generic React/Emotion box props are supplied with host CSS or a component's
styles property. The three layout primitives reproduce their complete React Box prop surface
directly because those props are their primary public contract.
react-parity.json records the required mapping and Storybook examples for every shipped port.
npm run check:web-components-ports fails when an element, required mapping, or parity story is
missing. Unit tests cover the programmatic property/event contract; the Storybook smoke test also
checks representative interactions, semantic output, and visual state for the interactive ports in Chromium.
There is no separate Web Components Storybook command. Run npm run build-storybook
then node bin/storybook/smoke-test.mjs to verify every native story and representative
interactions in Chromium. The Angular and Vue harnesses cover the complete supported catalog.
Start the development server for all Web Components examples:
npm install
npm run dev:web-componentsVite prints the local URL in the terminal (usually http://localhost:5173). The command does not select a framework or open a page by default. Choose the example you want in the browser:
- Angular:
http://localhost:5173/harness/fidelity-check.html - Vue:
http://localhost:5173/harness/fidelity-check-vue.html
Build the library with:
npm run build:web-components