BWSS (Boardweaver Style System) is a strict, structured subset of CSS. It is the style language for the two compiled page surfaces a game ships alongside its client: rules pages (/src/rules/*.bwss) and the marketing page (/src/marketing/*.bwss). Those are JSON documents the platform validates and serializes to safe HTML at commit time. Raw CSS strings are never accepted.
This section is the style-object reference. For the node sets each surface allows — both add Anchor, and the marketing page adds PlayButton, RulesButton and ThemeToggle on top of it — see v2-build-rules-instructions and v2-build-marketing-instructions.
BWSS does not apply to your game client. /src/frontend.tsx is a real React app: style it with ordinary CSS, inline styles, or whatever you import. Nothing here constrains it.
const node = {
type: "Box",
style: {
width: 120,
height: 80,
padding: 8,
borderRadius: 6,
backgroundColor: "$colors.surface.canvas", // theme-token ref
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "#222",
},
children: [
"Turn order", // text is a string child
{ type: "Image", src: "TrophyIcon", style: { width: 16, height: 16 } },
],
};
Shared by both page surfaces:
type |
Notes |
|---|---|
"Box" |
Generic container. Children: BWSS nodes or strings (text). |
"Image" |
{ src: GameImageKey, alt?, style? }. Resolved through the game's images map; unknown keys render nothing. |
"Svg" |
{ viewBox: {width,height}, paths: [{d, fill?, stroke?, ...}], style? }. Only <svg> + <path> are emitted. |
There is no Text node — text is a plain string in a Box's children array.
Boxtype BoxNode = { type: "Box"; style?: BwssStyle; children?: (BwssNode | string)[] };
On the rules surface Box also takes an id, the target of a #fragment link.
Imagetype ImageNode = { type: "Image"; src: GameImageKey; alt?: string; style?: BwssStyle };
src is a GameImageKey — a key for media already uploaded to the game. You don't invent keys; reference ones the game has. Unknown keys render nothing.
Svgtype SvgNode = {
type: "Svg";
viewBox: { width: number; height: number };
paths: SvgPathChild[];
style?: BwssStyle;
};
type SvgPathChild = {
d: string; // SVG path-data string
fill?: string;
stroke?: string;
strokeWidth?: number | string;
fillRule?: "nonzero" | "evenodd";
strokeLinecap?: "butt" | "round" | "square";
strokeLinejoin?: "miter" | "round" | "bevel";
opacity?: number | string;
};
<svg> and <path> are emitted. No <g>, <defs>, <linearGradient>, <use>, <foreignObject>, <script>, <text>, <image>.d is a character-class-validated string: digits, command letters (MLHVCSQTAZmlhvcsqtaz), whitespace, +-.eE,. Anything else (incl. <>"'();) is rejected.Svg; max 4000 chars per d; max 16,000 chars total across paths; viewBox sides ≤ 10,000.BwssStyle — the allow-listStrict (unknown properties throw). Categories below; values are typed.
display, flexDirection, flexWrap, justifyContent, alignItems, gap,
flex, flexBasis, flexGrow, flexShrink,
width, height, minWidth, maxWidth, minHeight, maxHeight,
padding, paddingX, paddingY, paddingTop, paddingRight, paddingBottom, paddingLeft,
margin, marginX, marginY, marginTop, marginRight, marginBottom, marginLeft,
position, top, right, bottom, left,
overflow
display: "flex" | "block" | "inline-flex" | "none". position: "relative" | "absolute". overflow: "hidden" | "auto" | "visible".
fontSize, lineHeight, letterSpacing,
fontWeight, fontStyle, fontFamily,
textAlign, textTransform, whiteSpace, textOverflow,
color, textGradient
fontWeight: "normal" | "bold" | "100"–"900". fontFamily: "sans" | "serif" | "mono" or a GameFontKey string. textAlign: "left" | "right" | "center" | "justify". textTransform: "none" | "uppercase" | "lowercase" | "capitalize". whiteSpace: "normal" | "nowrap" | "pre" | "pre-line" | "pre-wrap". textOverflow: "clip" | "ellipsis".
backgroundColor, backgroundImage, backgroundGradient,
backgroundSize, backgroundPosition, backgroundRepeat
backgroundGradient is a structured linear | radial array (max 4 layers). No raw CSS linear-gradient(...) strings.
borderRadius,
borderWidth, borderStyle, borderColor,
borderTopWidth, borderTopStyle, borderTopColor,
borderRightWidth, borderRightStyle, borderRightColor,
borderBottomWidth, borderBottomStyle, borderBottomColor,
borderLeftWidth, borderLeftStyle, borderLeftColor
borderStyle (and per-side): "none" | "solid" | "dashed" | "dotted". There is no border / borderTop shorthand — split into width / style / color.
boxShadow, opacity, filter, clipPath, transform
boxShadow: "none" OR an array of structured layers { offsetX, offsetY, blur?, spread?, color, inset? }.opacity: number in [0, 1] or "0%"–"100%".filter: array of single-key function objects: { blur }, { brightness }, { contrast }, { grayscale }, { hueRotate }, { invert }, { saturate }, { sepia }, { dropShadow: {...} }. Max 8 entries.clipPath: a single-key object: { polygon }, { circle }, { ellipse }, or { inset }.transform: array of single-key function objects: { rotate }, { translateX }, { translateY }, { scale }. Max 4 entries.color, backgroundColor, borderColor, boxShadow[*].color, dropShadow.color, Svg fill / stroke)One of:
"#abc" / "#abcdef" / "#abcdef12" (hex, 3/6/8 digits)"rgb(r, g, b)" / "rgba(r, g, b, a)" with numeric args"$colors.surface.canvas", "$semanticTokens.fg.muted", etc. (lt;segment>(.<segment>)+, alphanumerics + _)No url(), var(), calc().
width, padding, top, borderRadius, ...)One of:
number (treated as px)<number>(px|%|em|rem)$theme-token refNo calc(), no vh / vw / vmin / vmax / ch / ex.
A theme token ref starts with $, has at least two dotted segments, and only contains alphanumerics, _, and .:
$colors.surface.canvas // OK
$semanticTokens.fg.muted // OK
$spacing.lg // OK
$linear-gradient(...) // REJECTED — parens disallowed
$colors // REJECTED — needs ≥ 2 segments
The resolver returns values that have already been validated as colors / lengths. An unknown token resolves to nothing.
.strict(): an unknown style property (e.g. transform typoed as tranform, or background shorthand) is REJECTED, not silently dropped.border, background, font, transition, animation etc. are absent by design.Image.src is a GameImageKey string — unknown keys render nothing.Svg.paths[*].d is character-class restricted: digits, MLHVCSQTAZmlhvcsqtaz, whitespace, +-.eE, only. URLs, parens, and quote marks throw.Box is a plain string child — there's no Text node. To style text, set typography style fields on the enclosing Box.