goshtoso-charts

module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: MIT

README

Goshtoso Charts

Static/vector and opt-in interactive chart components for Go/templ applications using Goshtoso. The public contract stays renderer-neutral: typed configs, instances, and options do not expose a backing renderer. Initial product driver was Xisnove monitoring, but the library is product-neutral.

Charts use Goshtoso theme tokens, including Tailwind-compatible chart tokens, and responsive Goshtoso ActionGroup controls. Static/vector and interactive families deliberately differ where their proven export capabilities differ. Interactive runtime assets are vendored locally by default; CDN delivery is an explicit opt-in. Components provide accessible labels and adjacent exact data where a visual alone would be insufficient; interactive charts can also receive live Cartesian data through a renderer-neutral SSE snapshot contract.

Install

Requires Go 1.26.5+ and a Goshtoso-integrated templ application.

go get github.com/araihu/goshtoso-charts

Component contract

Every chart follows Goshtoso component shape: a typed Config, concrete Instance, stable Kind(), and templ.Component rendering. Extension-owned kinds live in github.com/araihu/goshtoso-charts/components; they deliberately do not modify Goshtoso's core kind registry.

Chart surfaces use Goshtoso tokens. Categorical series use Tailwind-compatible chart tokens (--color-chart-series-1 through --color-chart-series-8) so a theme's small semantic palette does not make unrelated series look equivalent. Surface, outline, grid, and text equivalents use the same --color-chart-* namespace. Tailwind arbitrary-variable utilities such as text-(--color-chart-series-1) and bg-(--color-chart-surface) can reuse them.

Chart palettes

Default charttheme.PaletteAuto detects AraiHu in SSR markup and adds a warm, contrasting palette around its lime accent. Themes without chart CSS receive the high-contrast Bold fallback. Neutral and Pastel fallbacks are also built in.

import "github.com/araihu/goshtoso-charts/components/charttheme"

style := charttheme.Style{
	Palette: charttheme.PalettePastel,
	Class:   "rounded-lg ring-1", // Tailwind utilities or an application class
}

Pass Style: style to SSR or typed interactive configs. Explicit ordered colors have highest priority:

Style: charttheme.Style{
	Palette: charttheme.PaletteAraiHu,
	Colors:  []string{"#2563eb", "oklch(70% 0.19 25)"},
	Class:   "my-chart",
},

Application CSS may target my-chart and override any --color-chart-series-* token. Interactive canvas charts resolve these CSS tokens through the private runtime bridge and refresh when Goshtoso theme or dark-mode state changes. Explicit Style.Colors remain authoritative.

Source policy

Examples preserve a concrete upstream example's dataset, hierarchy, and chart semantics before being adapted to typed Goshtoso APIs, accessibility, responsive layout, and theme tokens. Static/vector examples originate in go-analyze/charts; interactive examples originate in go-echarts/examples. The demo's Attributions page centrally records exact example paths, revisions, runtime, and geography sources.

Line chart

Use the chart as supporting evidence inside a Goshtoso panel.Panel; keep exact data in nearby text or a table when users need it.

package dashboard

import (
	"github.com/araihu/goshtoso/components/panel"
	"github.com/araihu/goshtoso-charts/components/line"
)

templ SignupTrend() {
	@panel.Panel(panel.Config{
		Header: templ.Raw("<h2>Signups</h2>"),
		Body: line.Line(line.Config{
			Label:   "Weekly signups",
			Caption: "Seven-day trend",
			Labels:  []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"},
			Series: []line.Series{{Name: "Signups", Values: []float64{12, 18, 15, 21, 24, 19, 28}}},
		}),
	})
}

Line charts render SVG geometry on the server, then resolve surfaces from Goshtoso tokens and series from chart tokens at display time. They follow theme and .dark surface changes without browser rendering or a fresh response.

Bar chart

Use bar charts for categorical comparisons such as deployment outcomes, monitor-result counts, or status breakdowns. Stacked keeps multiple named series within each category; leave it false for side-by-side comparisons.

@bar.Bar(bar.Config{
	Label: "Deployments by environment",
	Labels: []string{"Development", "Staging", "Production"},
	Series: []bar.Series{
		{Name: "Successful", Values: []float64{18, 12, 9}},
		{Name: "Failed", Values: []float64{1, 2, 1}},
	},
	Stacked: true,
})

Vertical remains the zero-value orientation. Set OrientationHorizontal for category labels beside the bars; horizontal charts include an adjacent exact value table.

@bar.Bar(bar.Config{
	Label:       "World population by reporting series",
	Title:       "World Population",
	Orientation: bar.OrientationHorizontal,
	Labels:      []string{"UN", "Brazil", "Indonesia", "USA", "India", "China", "World"},
	Series: []bar.Series{
		{Name: "2011", Values: []float64{10, 30, 50, 70, 90, 110, 130}},
		{Name: "2012", Values: []float64{20, 40, 60, 80, 100, 120, 140}},
	},
	Padding: bar.Padding{Top: 20, Right: 40, Bottom: 20, Left: 20},
	Width: 600, Height: 400,
})

Pie chart

Use pie charts for a small categorical distribution that is meaningful as parts of a total, such as retained monitor observation states. Avoid them for ordered time-series data or many categories.

@pie.Pie(pie.Config{
	Label: "Observation states",
	Slices: []pie.Slice{
		{Name: "Up", Value: 94},
		{Name: "Degraded", Value: 4},
		{Name: "Down", Value: 2},
	},
})

Keep exact values in nearby text or a table when readers need them.

Scatter chart

Use scatter charts for sparse observations or dense aligned samples across explicit ordered categories. This example adapts upstream examples/1-Painter/scatter_chart-3-dense_data/main.go with a fixed local RNG seed: three 1,000-category bounded random walks, repeated samples, SMA(100), and maximum references. Categories remain equally spaced keys.

@scatter.Scatter(scatter.Config{
	Label:      "Dense scatter data",
	Categories: labels,
	Width: 600, Height: 400,
	Options: scatter.Options{Size: 0.5, Trend: scatter.TrendLine{
		Kind: scatter.TrendSimpleMovingAverage, Period: 100,
	}},
	Series: []scatter.Series{
		{Name: "One", Values: values[0], Options: scatter.Options{ReferenceLine: scatter.ReferenceLineMaximum}},
		{Name: "Two", Values: values[1], Options: scatter.Options{ReferenceLine: scatter.ReferenceLineMaximum}},
		{Name: "Three", Values: values[2]},
	},
})

Values aligns zero, one, or many samples to each category without repeating category strings. Sparse Points remains supported; one series cannot mix both. For large datasets, keep exact data in a caller-owned table, download, or drill-down instead of expanding thousands of values into the chart DOM.

Shared controls and export

Every current chart accepts the same renderer-neutral control contract. All charts default to Expand plus capability-derived Export. Fullscreen remains an independent opt-in. Controls require the local assets.Handler() mount shown below; Goshtoso's head dependencies provide ActionGroup measurement.

import "github.com/araihu/goshtoso-charts/components/chartcontrol"

staticCfg.Controls = chartcontrol.Options{
	Fullscreen: true,
}
staticCfg.Export = &chartcontrol.ExportOptions{
	Filename: "weekly-signups",
}

interactiveCfg.Options.Controls = staticCfg.Controls
interactiveCfg.Options.Export = staticCfg.Export

// Explicit opt-outs remain renderer-neutral.
staticCfg.Controls.Expand = chartcontrol.Bool(false)
staticCfg.Export = &chartcontrol.ExportOptions{Disabled: true}

Static Line, Bar, Pie, Scatter, and Radar expose one Goshtoso Export dropdown with SVG and PNG. Current interactive components, including Sunburst, expose one direct PNG Export button. Zero proven formats render no Export control; one renders a direct button; more than one renders a Goshtoso Dropdown. Unsupported explicit formats fail rendering instead of creating dead controls. Expand and fullscreen preserve chart DOM, interaction state, and live SSE instances. When Expand and Fullscreen are both enabled, one stacked Expand Dropdown offers both choices at wide widths. Goshtoso ActionGroup keeps Expand primary and flattens stacked Fullscreen and export actions into one icon-only overflow Dropdown as space contracts; menus never nest. See the capability matrix, layout contract, and pending integration checks.

Candlestick

candlestick.Candlestick renders typed OHLC data as accessible SSR SVG. Each datum must satisfy Low <= Open/Close <= High; an adjacent table exposes exact values and increase/decrease text without relying on color.

@candlestick.Candlestick(candlestick.Config{
	Label:      "Seven-day stock price",
	Title:      "Candlestick Chart",
	SeriesName: "Stock Price",
	Data: []candlestick.Datum{
		{Label: "Day 1", Open: 100, High: 110, Low: 95, Close: 105},
		{Label: "Day 2", Open: 105, High: 115, Low: 100, Close: 112},
	},
})

Interactive components

Interactive components expose only typed Goshtoso Charts configs. Renderer types and callbacks are implementation details and cannot cross the public API.

Interactive charts require one browser runtime tag. Local, vendored delivery is the default:

import chartassets "github.com/araihu/goshtoso-charts/assets"

mux.Handle("GET "+chartassets.Prefix, chartassets.Handler())
import "github.com/araihu/goshtoso-charts/components/dependencies"

templ Layout() {
	<head>
		@dependencies.Dependencies()
	</head>
}

Mount assets.Handler() directly; it already removes /charts/assets/. Applications choosing third-party delivery must opt in explicitly with dependencies.WithCDN(). See interactive chart dependencies for custom paths, SRI, CSP, and load-order details.

@interactive.Bar(interactive.BarConfig{
	Label: "Weekly deployments",
	XAxis: []string{"Mon", "Tue"},
	Series: []interactive.BarSeries{{
		Name: "Production",
		Data: []interactive.BarData{{Value: 3}, {Value: 5}},
	}},
})

Word clouds keep weighted words, silhouettes, sizing, rotation, layout, and semantic classes in a typed renderer-neutral config. Exact values remain in a bounded adjacent table.

@interactive.WordCloud(interactive.WordCloudConfig{
	Label: "Weighted search terms",
	Series: interactive.WordCloudSeries{
		Name: "terms",
		Words: []interactive.Word{
			{Name: "Sam S Club", Value: 10000, Class: "retail"},
			{Name: "Macys", Value: 6181, Color: "#ff8a3d"},
		},
		Options: interactive.WordCloudSeriesOptions{
			Shape:     interactive.WordCloudShapeStar,
			SizeRange: &interactive.WordCloudSizeRange{Min: 14, Max: 80},
		},
	},
})

Use application-owned values only. Shared ChartOptions, SeriesOptions, and component-specific typed variants provide controlled customization without exposing the backing renderer.

Parallel coordinates use ordered typed dimensions and aligned observations. Numeric and categorical values cannot be mixed accidentally:

chart := interactive.Parallel(interactive.ParallelConfig{
	Label: "Air quality profiles",
	Dimensions: []interactive.ParallelDimension{
		{Name: "AQI", Range: &interactive.ParallelRange{Max: interactive.Float(300)}},
		{Name: "Level", Categories: []string{"Good", "Moderate", "Heavily"}},
	},
	Series: []interactive.ParallelSeries{{
		Name: "Beijing",
		Observations: []interactive.ParallelObservation{{
			Name: "Day 1",
			Values: []interactive.ParallelValue{
				interactive.ParallelNumber(55),
				interactive.ParallelCategory("Moderate"),
			},
		}},
	}},
})
Live Cartesian data

Interactive Bar and Line components can opt into an SSE source. Each named event carries a complete renderer-neutral interactive.CartesianSnapshot; full replacement makes reconnects deterministic and avoids exposing renderer update structures.

config.Live = &interactive.LiveData{
	URL:   "/metrics/events",
	Event: "chart",
}

Maps use one component for Brazil's 26 states and Federal District. Region names, UF codes, and values remain typed; caller colors or semantic classes stay programmatic; exact values remain available beside the canvas.

@interactive.Map(interactive.MapConfig{
	Label:    "Brazil states",
	Geometry: interactive.MapGeometryBrazil,
	Variant:  interactive.MapVariantScale,
	Series: interactive.MapSeries{
		Name: "Brazil state values",
		Regions: brazilStateValues, // all 26 states plus DF
	},
})

Geo stays distinct from Map: it plots typed longitude/latitude/value points over pinned Brazil-state or São Paulo-municipality geometry. Scatter and effect-scatter behavior remain series kinds on one component; visual ranges use theme cold-to-warm tokens unless callers provide explicit colors.

@interactive.Geo(interactive.GeoConfig{
	Label:    "São Paulo cities",
	Geometry: interactive.GeoGeometrySaoPaulo,
	VisualRange: &interactive.GeoVisualRange{
		Min: 0, Max: 100, Calculable: interactive.Bool(true),
	},
	Series: []interactive.GeoSeries{{
		Name: "geo",
		Kind: interactive.GeoSeriesScatter,
		Points: []interactive.GeoPoint{
			{Name: "São Paulo", Longitude: -46.63, Latitude: -23.55, Value: 12},
			{Name: "Campinas", Longitude: -47.06, Latitude: -22.91, Value: 76},
			{Name: "Ribeirão Preto", Longitude: -47.81, Latitude: -21.18, Value: 41},
		},
	}},
})

The demo's live-availability page uses this primitive with narrow stacked bars. Availability remains application semantics and is not a separate chart type.

Roadmap

Current library covers a broad static/vector family (including line, bar, pie, scatter, radar, candlestick, funnel, heatmap, table, and violin) and an opt-in interactive family with Cartesian, hierarchy, distribution, map/geo, and 3D variants. Brazil-state and São Paulo-municipality geometry are bundled for the typed map and geo contracts; live availability remains an application use of the shared live Cartesian contract, not a separate chart type.

Future primitives need a concrete use case and upstream source, stable renderer-neutral kind, accessible exact evidence, semantic-token palette, responsive behavior, capability-derived controls/export, and focused tests.

See chart-library evaluation, surface brief, and Xisnove heartbeat brief.

Development

templ generate
go test ./...
git diff --exit-code

Demo site

site/ is a separate consumer module, shaped after the Goshtoso component demo catalog. It mounts Goshtoso assets at /assets/, Goshtoso Charts assets at /charts/assets/, and uses both dependency components.

cd site
GOWORK=off go run ./cmd/server

Open http://localhost:8091.

Directories

Path Synopsis
Package assets serves versioned browser assets used by Goshtoso Charts.
Package assets serves versioned browser assets used by Goshtoso Charts.
Package components defines stable identities shared by Goshtoso Charts components.
Package components defines stable identities shared by Goshtoso Charts components.
bar
templ: version: v0.3.1020
templ: version: v0.3.1020
candlestick
templ: version: v0.3.1020
templ: version: v0.3.1020
chartcontrol
Package chartcontrol provides renderer-neutral controls shared by every chart.
Package chartcontrol provides renderer-neutral controls shared by every chart.
charttheme
templ: version: v0.3.1020
templ: version: v0.3.1020
dependencies
Package dependencies renders the browser runtime required by interactive Goshtoso Charts components.
Package dependencies renders the browser runtime required by interactive Goshtoso Charts components.
funnel
templ: version: v0.3.1020
templ: version: v0.3.1020
heatmap
templ: version: v0.3.1020
templ: version: v0.3.1020
interactive
templ: version: v0.3.1020
templ: version: v0.3.1020
line
templ: version: v0.3.1020
templ: version: v0.3.1020
pie
templ: version: v0.3.1020
templ: version: v0.3.1020
radar
templ: version: v0.3.1020
templ: version: v0.3.1020
scatter
templ: version: v0.3.1020
templ: version: v0.3.1020
table
templ: version: v0.3.1020
templ: version: v0.3.1020
violin
Package violin renders accessible server-side SVG violin charts.
Package violin renders accessible server-side SVG violin charts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL