Skip to content
RANDSUMui

Getting Started with @randsum/ui

Terminal window
npm install @randsum/ui

It rolls with @randsum/roller, so a roll in the element follows exactly the rules a roll in code does. The elements are built with Lit and carry their own styles in Shadow DOM: there is no stylesheet to import.

Import an element once and use its tag. Nothing else is needed, which is why htmx and Astro have no wrapper: an element htmx swaps in upgrades by itself.

<script type="module">
import '@randsum/ui/roller'
</script>
<randsum-roller size="compact" notation="2d20H+5"></randsum-roller>
<script>
document.querySelector('randsum-roller').addEventListener('randsum-roll', event => {
console.log(event.detail.total)
})
</script>

Lists such as presets and keys can be set as JSON attributes, or as properties from script:

<randsum-roller
presets='[{"label":"Advantage","notation":"2d20H"},{"label":"Stats","notation":"4d6L"}]'
></randsum-roller>
import { Roller } from '@randsum/ui/react/roller'
<Roller size="mini" defaultNotation="4d6L" onRoll={result => console.log(result.total)} />

React 18 and 19 both work: the wrapper sets properties and listens for events itself. notation with onChange keeps the value in your state, and each new rollSignal rolls on demand.

<script setup>
import { ref } from 'vue'
import { Roller } from '@randsum/ui/vue/roller'
const notation = ref('4d6L')
</script>
<template>
<Roller size="compact" v-model="notation" @roll="result => console.log(result.total)" />
</template>

Svelte 5 handles custom elements directly, so there is no wrapper. Import the element, and @randsum/ui/svelte once for its types:

<script lang="ts">
import '@randsum/ui/roller'
import type {} from '@randsum/ui/svelte'
</script>
<randsum-roller size="mini" notation="4d6L" onrandsum-roll={e => console.log(e.detail.total)} />

React Native has no DOM, so custom elements cannot run there. @randsum/ui/native/roller is the same roller built from native views, on the same state, with the same three sizes and the React Roller’s props. It needs react-native 0.76 or later, and works in Expo as is.

import { Roller } from '@randsum/ui/native/roller'
export function Dice() {
return <Roller size="compact" defaultNotation="4d6L" onRoll={result => console.log(result.total)} />
}

There is no localStorage, so max keeps its history in memory. Pass onHistoryChange to save each change (to AsyncStorage, say) and initialHistory to start from what you saved. The colours follow the device’s light or dark scheme, and RollerThemeProvider changes any token:

import { RollerThemeProvider } from '@randsum/ui/native/theme'
<RollerThemeProvider theme={{ accent: '#0f766e' }}>
<Roller size="max" />
</RollerThemeProvider>

Custom elements normally draw themselves once their script runs. @randsum/ui/ssr renders them on the server instead, with each shadow root filled in as Declarative Shadow DOM, so the roller is on the page in the first frame, before any script loads. When the element’s script does run, it swaps the server copy for the live one in place.

Terminal window
npm install @lit-labs/ssr
import { renderToString } from '@randsum/ui/ssr'
const html = await renderToString(
'<randsum-roller size="mini" notation="4d6L"></randsum-roller>'
)
// Send `html` in your page, and load '@randsum/ui/roller' in the browser.

It works in any server that runs JavaScript, so an htmx backend can return rendered rollers in its fragments. In Astro, put the result in <Fragment set:html={html} />; the rollers on this site are rendered this way.

Every colour, font and radius is a --rsui-* custom property. Custom properties inherit through Shadow DOM, so set them on the element or on any ancestor:

randsum-roller {
--rsui-accent: #0f766e;
--rsui-radius-md: 4px;
}

Colours use light-dark(), so set color-scheme on your page and the elements follow it.

Property (attribute) Type Notes
size 'mini' | 'compact' | 'max' Defaults to 'compact'
notation string Setting it replaces what the user typed
keys readonly Key[] Defaults to DEFAULT_KEYS; always shown
presets readonly Preset[] Optional named rolls, behind a switch; keys first
presetsLabel (presets-label) string What the presets are called. Defaults to “Presets”
presetsLayout (presets-layout) 'chips' | 'list' Compact: 'list' shows each description
placeholder string The empty field’s hint
appLink (app-link) string Mini and Compact: a link to the full roller
history boolean Max: recent rolls and a History dialog. Defaults to true
storageKey (storage-key) string Max: keeps history in localStorage
historyLimit (history-limit) number Max: how many rolls history keeps. Defaults to 50

Compact also has a footer slot. The element fires randsum-change (detail { notation }) on every edit and randsum-roll (detail: the result) after each roll, and has a roll() method.

The React wrapper takes the same props in camelCase, plus defaultNotation, onChange, onRoll, rollSignal, footer and className. The Vue wrapper takes them with v-model for the notation and emits change and roll.

Every size shows dice keys. Pass keys to change them, and presets to add named rolls:

import { Roller } from '@randsum/ui/react/roller'
import { modifierKeys } from '@randsum/ui/keys'
import { modifierPresets } from '@randsum/ui/presets'
<Roller
defaultNotation="3d6!"
keys={modifierKeys('explode')}
presets={modifierPresets('explode')}
/>

modifierKeys(name) and modifierPresets(name) give each modifier’s own keys and its documented examples, the same ones on the modifiers page.