Skip to main content

Custom Plugin (Typed)

This recipe shows how to build a typed Boss plugin and wire it into your config.

Minimal typed plugin

// my-plugin.ts
import type { Plugin } from 'boss-css'

export const name = 'my-plugin'

export const onBoot: Plugin<'onBoot'> = async api => {
api.log.child('plugin').child(name).log('Boot')
}

Emit CSS from props

import type { Plugin } from 'boss-css'

export const name = 'spacing-clamp'

export const onProp: Plugin<'onProp'> = async (api, { name, prop, contexts }) => {
if (name !== 'padding') return
const value = prop?.value
if (typeof value !== 'number') return

api.css.selector({
className: api.contextToClassName(name, value, contexts, true, api.selectorPrefix),
query: prop.query ?? null,
})
api.css.rule('padding', `clamp(8px, ${value}px, 32px)`)
api.css.write()
}

Add dictionary entries

import type { Plugin } from 'boss-css'

export const name = 'custom-props'

export const onBoot: Plugin<'onBoot'> = async api => {
api.dictionary.set('bleed', {
property: 'margin',
aliases: ['bleed'],
description: 'Negative margin helper',
})
}

Runtime hooks (browser)

Use onBrowserObjectStart to transform props into runtime output:

import type { Plugin } from 'boss-css'

export const name = 'runtime-example'

export const onBrowserObjectStart: Plugin<'onBrowserObjectStart'> = (api, { input, output = {} }) => {
if (!('cursor' in input)) return
output.style = { ...(output.style as Record<string, unknown>), cursor: input.cursor }
}

Add plugin to config

// .bo$$/config.js
import * as myPlugin from './my-plugin'

export default {
plugins: [
// ...boss plugins
myPlugin,
],
}

Tips

  • Use Plugin<'onProp'> (or other hook types) for full type safety.
  • Most server-side work is done in onPropTree or onProp.
  • Keep runtime work small and gated to dynamic values.