{}

Packaging as a library

A widget package — whether it’s a small in-app helpers file or a separately-versioned library — follows a consistent shape so consumers can mix it into their factory in two lines. This page is the contract.

File layout

One widget per .go file, named after the widget. Optional CSS and JS siblings carrying the same base name. A doc.go for the package doc and a factory.go for the factory type and asset registration:

myorg-widgets/
├── doc.go              ← package doc with import path, license, footprint
├── factory.go          ← MyOrgFactory + asset registration in init()
├── mywidget.go         ← MyWidgetWidget + builder methods + Draw
├── mywidget.css        ← optional sibling CSS
├── mywidget.js         ← optional sibling JS
└── anotherwidget.go    ← one widget per file

This is the layout every package under the framework root uses — see basic/, form/, or any of the optional packages (chart/, code/, richedit/).

The factory file

Four responsibilities, all in one short file:

package myorg

import (
    "embed"

    "github.com/microbus-io/bespa/basic"
    "github.com/microbus-io/bespa/widget"
)

// MyOrgFactory aggregates the widget constructors of this package.
type MyOrgFactory struct{}

// factory is a private composite the package uses internally to construct
// nested framework widgets.
var factory = struct {
    widget.WidgetFactory
    basic.BasicFactory
    MyOrgFactory
}{}

// Convenience aliases — let the Draw methods in this package write
// Tag(…) and Text(…) instead of factory.Tag(…) and factory.Text(…).
// Not the public entry point; consumers compose MyOrgFactory into their
// own factory struct (see "How consumers compose" below).
var Any = factory.Any
var Many = factory.Many
var Text = factory.Text
var HTML = factory.HTML
var HTMLUnsafe = factory.HTMLUnsafe
var Bytes = factory.Bytes
var Tag = factory.Tag

type Widget = widget.Widget
type InputWidget = widget.InputWidget
type BytesWidget = widget.BytesWidget

// Bundle every static asset the package ships. Globbing by extension
// keeps .go source files (and READMEs, .txt, etc.) out of the binary.
// Add or remove extensions to match what the package contains — each
// pattern must match at least one file.
//go:embed *.css *.js
var bundle embed.FS

func init() {
    widget.AssetRegistry.RegisterFS(bundle)
}

How consumers compose

From the calling side, your widgets fall in next to the framework ones:

import (
    "github.com/microbus-io/bespa"
    "github.com/myorg/myorg-widgets"
)

// Compose MyOrgFactory alongside the framework defaults:
var wf = struct {
    bespa.DefaultFactory
    myorg.MyOrgFactory
}{}

// Now wf.MyWidget(...) is available with full type-safety on the chain.

Because MyOrgFactory is at the shallowest depth in the composite struct, Go’s selector resolution picks its methods over any same-named method on a more deeply embedded factory. That’s how you can override a built-in widget by providing a same-named constructor.

Name collisions

If two embedded factories both define a method with the same name — say Button — Go’s selector resolution falls back to the shallowest match. When two libraries collide at the same depth, go build reports an ambiguous-selector error. Two ways out:

For a published library, document the methods it exports. Two libraries can be composed without surprises as long as their surface-area docs are honest about what names they claim.

Versioning

Semantic Versioning, the usual rules:

Naming the binary cost

If your package bundles anything weighty — a JS library, a font, large GeoJSON — say so in the package doc:

// Package mywidget embeds Foo.js (~800 KB, MIT). Importing this
// package adds ~1 MB to your binary. See ATTRIBUTIONS.md.
package mywidget

Consumers should know what they’re taking on. The pattern in this codebase is that anything over ~100 KB earns its own opt-in package rather than living in basic/.

See also

Assets & CSS — the asset-registration API the init() calls into.

Material theming — how widget CSS should reference design tokens.

For a worked example, read the entirety of chart/ — it’s a single-widget package with a JS dependency, CSS, a dynamic-asset handler, and a clean factory composition.