design system

ds

one design language, one implementation, every app on mianda.dev. tokens and components ship as framework-agnostic custom elements — link two files, import a module, or install the package. this page is built out of them.

v1

use it

The script is deliberately render-blocking: it applies the stored theme before first paint and defines the elements before <body> parses, so nothing flashes and nothing shifts.

<!-- drop-in: any framework, any host, no build step -->
<link rel="stylesheet" href="https://ds.mianda.dev/v1/ds.css">
<script src="https://ds.mianda.dev/v1/ds.js"></script>

<dev-header app="shaderlab"></dev-header>
<dv-button variant="primary" size="lg">generate</dv-button>
<dev-footer app="shaderlab" version="0.3.0"></dev-footer>
// as a module, when you want the api too
import { theme, loadConfig } from 'https://ds.mianda.dev/v1/ds.mjs';

theme.toggle();
theme.subscribe(({ theme }) => console.log('now', theme));
// as a package, when you already have a bundler
npm install @mianda/ds

import '@mianda/ds';          // registers every element
import '@mianda/ds/ds.css';
import { theme, type ThemeName } from '@mianda/ds';

buttons

<dv-button> owns a real <button> — or an <a> when you give it href — and steps out of layout itself, so your flex parent sees the control, not a wrapper.

primary tonal outline ghost inverse disabled link
sm md lg ×
łączę connecting loading disables the control and sets aria-busy
<dv-button variant="primary" size="lg">generate</dv-button>
<dv-button href="/somewhere">open</dv-button>
<dv-button loading>connecting</dv-button>
<dv-icon-button label="fullscreen">⛶</dv-icon-button>

chips

A selectable filter token — denser than a button, interactive unlike a tag. Controlled: reflect your state onto selected and listen for the click, which bubbles from the native button inside.

webgl glsl motion archived selected: webgl
<dv-chip selected>webgl</dv-chip>

badges & tags

live accent wip webgl glsl worker
<dv-badge dot>live</dv-badge>
<dv-tag>webgl</dv-tag>

tabs

Emits change with detail.value; also readable and settable as .value.

value: lab
<dv-tabs items="generate,lab,library" value="lab"></dv-tabs>

tabs.addEventListener('change', (e) => console.log(e.detail.value));

inputs & switch

checked: true
<dv-input label="prompt" hint="plain language" placeholder="…"></dv-input>
<dv-switch label="loop preview" checked></dv-switch>

input.value            // proxies the native control
sw.addEventListener('change', (e) => e.detail.checked);

cards & placeholder

A card with href becomes one link end to end, keeping the whole surface clickable without nesting an anchor around a div.

static

plain card

the class lands on the host — no wrapper between this and the grid.

interactive

hover me

steps a surface and border on hover, scales .99 on press.

linked

the hub →

one anchor around the whole card.

preview 1920×1080

tokens

CSS custom properties on :root, re-declared under [data-theme="dark"]. Read them, never hard-code their values. One accent hue — rank and state are carried by surface, weight and fill instead.

tokenvalue

shell

The header and footer at the top and bottom of this page are the same elements every app renders. The app switcher is fetched from the hub, so shipping a new app updates every app's menu without redeploying any of them.

<dev-header app="shaderlab" root="#/" shape="asym"></dev-header>
<dev-footer app="shaderlab" version="0.3.0"></dev-footer>

app     name in the wordmark            suffix  "" for none
home    where the ⌂ button goes         root    wordmark target, "#/" on an SPA
shape   pills | asym | angle            config  registry url, or "off"