Semiotic provides multiple layers of visual control: high-levelcolorBy / colorScheme on every HOC chart, data-driven style functions for per-mark customization, CSS custom properties for global theming, and SVG overlay layers for custom graphics. These compose freely — set a theme, override one chart's palette, then fine-tune individual marks.
Color Schemes
Every HOC chart accepts colorBy (field name or function) and colorScheme (array of colors). These override the active theme's categorical palette for a single chart:
JSX
import { BarChart, LineChart } from "semiotic" const palette = ["#2563eb", "#0d9488", "#ea580c", "#6b7280"] <BarChart data={data} categoryAccessor="region" valueAccessor="revenue" colorBy="region" colorScheme={palette} /> <LineChart data={lines} xAccessor="x" yAccessor="y" lineBy="id" colorBy="id" colorScheme={palette} />
Data-Driven Styling
For per-mark customization, use style functions via frameProps. The function receives each data element and its index, returning an SVG style object. This works on all frame types:
Conditional Bar Colors
Green = met target, Red = below target
Opacity Encoding
Bright = above threshold, Faded = below. Colors from colorBy.
JSX
// Conditional fill based on data values // Use pieceStyle (not style) for ordinal charts via frameProps <BarChart data={data} categoryAccessor="region" valueAccessor="revenue" frameProps={{ pieceStyle: (d) => ({ fill: d.revenue >= d.target ? "#22c55e" : "#ef4444", stroke: "white", strokeWidth: 1, }), }} /> // Layered encoding: colorBy sets fill, pointStyle adds opacity // pointStyle without a "fill" key preserves colorBy colors <Scatterplot data={points} xAccessor="x" yAccessor="y" sizeBy="size" colorBy="group" frameProps={{ pointStyle: (d) => ({ fillOpacity: d.above ? 0.9 : 0.3, stroke: d.above ? "white" : "none", strokeWidth: d.above ? 1.5 : 0, }), }} />
Style Prop Names by Frame Type
| Frame | Style Props |
|---|
StreamXYFrame | lineStyle, pointStyle, summaryStyle |
StreamOrdinalFrame | pieceStyle, summaryStyle, connectorStyle |
StreamNetworkFrame | nodeStyle, edgeStyle |
| HOC charts | Pass any of the above via frameProps |
Foreground & Background Graphics
Every Frame has two SVG layers for custom graphics:backgroundGraphics (behind data marks) andforegroundGraphics (on top). Pass static SVG or a function receiving { size, margin }.
Coordinate system: Graphics render in thechart area coordinate space — (0, 0) is the top-left corner of the chart area (inside margins), and the chart area extends to(size[0] - margin.left - margin.right, size[1] - margin.top - margin.bottom).
import { StreamXYFrame } from "semiotic" () => ({ stroke: "#ac58e5", strokeWidth: 2.5 }) const frameProps = { /* --- Data --- */ data: [{ label: "Revenue", coordinates: [{ month: 1, value: 12 }, { month: 2, value: 18 }, ... ] }], chartType: "line", /* --- Size --- */ margin: { top: 30, bottom: 40, left: 50, right: 20 }, /* --- Process --- */ xAccessor: "month", yAccessor: "value", lineDataAccessor: "coordinates", /* --- Customize --- */ lineStyle: lineStyle, title: "Background & Foreground Layers", showAxes: true, foregroundGraphics: ({ size, margin }) => { const w = size[0] - margin.left - margin.right const yPos = (size[1] - margin.top - margin.bottom) * 0.42 return ( <g> <line x1={0} y1={yPos} x2={w} y2={yPos} stroke="#E0488B" strokeWidth={1} strokeDasharray="4 4" /> <text x={w - 40} y={yPos - 6} fill="#E0488B" fontSize={11} fontWeight={600}> Target </text> </g> ) }, backgroundGraphics: ({ size, margin }) => { // Coordinates are in chart-area space: (0,0) = top-left of chart area const w = size[0] - margin.left - margin.right const h = size[1] - margin.top - margin.bottom return ( <g> <rect x={0} y={0} width={w} height={h} fill="#f4f0ff" rx={4} /> <text x={w / 2} y={h / 2} textAnchor="middle" fill="#d0c4ee" fontSize={48} fontWeight={700} opacity={0.5}> DRAFT </text> </g> ) } } export default () => { return <StreamXYFrame {...frameProps} /> }
CSS Class Names
For external stylesheet-driven styling, assign class names to marks. These accept strings or functions for data-driven class assignment:
JSX
// Static class <StreamXYFrame lineClass="revenue-line" /> // Dynamic class based on data <StreamXYFrame lineClass={(d) => `line-${d.category}`} pointClass={(d) => d.highlighted ? "point-active" : "point-default"} /> /* In your stylesheet */ .revenue-line { stroke: #ac58e5; stroke-width: 2; } .point-active { fill: #E0488B; r: 6; } .point-default { fill: #ccc; r: 3; }
Gradients & Patterns
Use additionalDefs to inject SVG<defs> (gradients, patterns, filters) into the frame, then reference them by ID in style functions:
JSX
<BarChart data={data} categoryAccessor="region" valueAccessor="revenue" frameProps={{ additionalDefs: [ <linearGradient key="g" id="bar-gradient" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stopColor="#8b5cf6" /> <stop offset="100%" stopColor="#ec4899" /> </linearGradient>, <pattern key="p" id="stripes" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"> <rect width="3" height="6" fill="#06b6d4" /> </pattern>, ], pieceStyle: (d) => ({ fill: d.revenue > 130 ? "url(#bar-gradient)" : "url(#stripes)", }), }} />
Canvas Hatch Patterns
SVG additionalDefs patterns don't work on canvas-rendered charts. For canvas fills, use createHatchPattern to generate a repeating diagonal-line CanvasPattern that can be passed as a fill value in any style function.
Hatched Bars (below target)
Solid = met target, Hatched = below target
Multiple Hatch Colors
Solid = met, Blue hatch = close, Red hatch = far below
JSX
import { BarChart, createHatchPattern } from "semiotic" // Create a hatch pattern (call once, reuse across renders) const hatch = createHatchPattern({ background: "#4e79a7", // tile background color stroke: "#fff", // diagonal line color lineWidth: 1.5, // line thickness (default 1.5) spacing: 6, // distance between lines (default 6) angle: 45, // line angle in degrees (default 45) }) <BarChart data={data} categoryAccessor="region" valueAccessor="revenue" frameProps={{ pieceStyle: (d) => ({ fill: d.revenue < d.target ? hatch : "#4e79a7", }), }} />
Note:createHatchPattern requires a browser environment (canvas). It returns null during SSR or in test environments where canvas is unavailable. The pattern is a nativeCanvasPattern object — assign it directly asfill in any pieceStyle, nodeStyle,edgeStyle, or pointStyle function.
Options
| Option | Type | Default | Description |
|---|
background | string | "transparent" | Background color of the pattern tile |
stroke | string | "#000" | Color of the diagonal lines |
lineWidth | number | 1.5 | Width of the diagonal lines in pixels |
spacing | number | 6 | Distance between lines in pixels |
angle | number | 45 | Line angle in degrees (0 = horizontal, 45 = diagonal, 90 = vertical) |
Styling Hierarchy
When multiple styling mechanisms are active, they compose in this order (later wins):
| Layer | Scope | Mechanism |
|---|
| 1. CSS custom properties | All charts in ancestor | --semiotic-* vars on any ancestor, orThemeProvider |
2. colorScheme | Single chart | Overrides theme categorical palette |
| 3. Style functions | Individual marks | frameProps.style, lineStyle, etc. |
| 4. CSS classes | Individual marks | className, lineClass — external CSS |
Props Reference
| Prop | Type | Required | Default | Description |
|---|
colorBy | string | function | — | undefined | Field name or function to map data to categorical colors. Used by all HOC charts. |
colorScheme | string[] | — | theme categorical | Array of colors for the categorical scale. Overrides theme colors for a single chart. |
pieceStyle / lineStyle / pointStyle / nodeStyle / edgeStyle | object | function | — | {} | Inline SVG style applied to marks. When a function, receives the data element. Use for fill, stroke, opacity, etc. On HOC charts, pass via frameProps (e.g. frameProps={{ pieceStyle: fn }} for ordinal, frameProps={{ pointStyle: fn }} for XY). Omitting 'fill' lets colorBy colors show through. |
className / lineClass / pointClass | string | function | — | "" | CSS class applied to marks. When a function, receives the data element. Use for external CSS styling. |
foregroundGraphics | ReactNode | function | — | null | SVG elements rendered on top of all data marks. Can be a function receiving { size, margin }. Coordinates are in the chart area coordinate space (origin at top-left of chart area, not the SVG edge). |
backgroundGraphics | ReactNode | function | — | null | SVG elements rendered behind all data marks. Can be a function receiving { size, margin }. Same coordinate space as foregroundGraphics. |
renderMode | "sketchy" | function | — | undefined | Set to "sketchy" to render marks in a hand-drawn style. Can be a function that returns "sketchy" per data item. Pass via frameProps on HOC charts. |
additionalDefs | array | — | [] | Array of SVG <defs> elements (gradients, patterns, filters) injected into the frame's SVG. Reference via url(#id) in style props. |