Have more questions? Join our Discord

Write the rules pages

Players read the rules pages in the Rules drawer, in the lobby and during a match, and on the game's public rules link. Follow these steps in order.

1. Plan the pages

  • /src/rules/index.bwss is the first page. Without it the game has no rules for players: the Rules button is hidden.
  • Each other page is /src/rules/<slug>.bwss. Name it with a letter first, then letters, digits, _ and -, such as setup or turn-order.
  • Players reach other pages only through links you write. There is no automatic table of contents.

Done when you have a list of page slugs and each page's purpose, and index links to every other page.

2. Upload the images

Upload each image with upload_art under a key such as BoardOverview: a capital letter first, then letters and digits. Images are JPEG or PNG, at most 5 MB.

Done when every image the pages show has an art key.

3. Write each page

A page is a JSON document: one node, whose children are nodes and plain strings. Write it with write_file. Strings render as plain text, with no markdown.

{
  "type": "Box",
  "style": { "display": "flex", "flexDirection": "column", "gap": 16, "color": "$semanticTokens.text.primary" },
  "children": [
    { "type": "Box", "id": "goal", "style": { "fontSize": 28, "fontWeight": "700" }, "children": ["Goal"] },
    { "type": "Box", "children": ["Be the first to get three marks in a row."] },
    { "type": "Image", "src": "BoardOverview", "alt": "The board at the start" },
    { "type": "Anchor", "href": "setup#board", "style": { "textDecorationLine": "underline" }, "children": ["Setup"] }
  ]
}

The nodes and styles are below. Colors from the theme are in Design the theme.

Done when each page is written and every rule a new player needs is on a page.

4. Check the pages

Run validate_code. It compiles every page and answers each problem as one line with the file and the path to the node:

/src/rules/index.bwss: children[0].style: Unrecognized key(s) in object: 'border'

write_file does not check a page; only validate_code and commit do. A link to a page that does not exist is not checked: check each link by hand.

Done when validate_code answers no page errors.

5. Commit

Run commit. A failed page compile answers RULES_COMPILE_FAILED with the errors.

Done when commit succeeds.

Nodes

Node Props Renders as
Box id?, style?, children? a block
Image src (an art key), alt?, style? an image. An unknown key fails the compile.
Svg viewBox: { width, height }, paths (1 to 16), style? a vector drawing
Anchor href, id?, style?, children? a link
  • Every node needs type. Every other key is checked: an unknown key is an error.
  • An id starts with a letter, then letters, digits, _ and -. A link reaches it as #id.
  • href is one of #id, slug or slug#id, at most 256 characters. Links to other sites are not allowed.
  • An Svg path is { d, fill?, stroke?, strokeWidth?, fillRule?, strokeLinecap?, strokeLinejoin?, opacity? }. d uses only path commands and numbers, at most 4000 characters.
  • Every node renders as plain blocks, with no heading or list elements. Make a heading with fontSize and fontWeight. Keep line breaks with "whiteSpace": "pre-line".

Styles

A style takes only the properties below. Values are single values: no shorthands such as border or "16px 24px", and no vh, vw, calc or var.

  • Values. A length is a number (pixels) or a string in px, %, em or rem. A color is #hex, rgb(), rgba(), a basic name such as white, or a theme token such as $semanticTokens.text.primary. fontWeight is a string: "700".
  • Layout: display (flex, block, inline-flex, none), flexDirection, flexWrap, justifyContent, alignItems, gap, flex, flexBasis, flexGrow, flexShrink, width, height, minWidth, maxWidth, minHeight, maxHeight, padding, paddingX, paddingY, paddingTop, paddingRight, paddingBottom, paddingLeft, the same for margin, position (relative, absolute), top, right, bottom, left, overflow.
  • Text: fontSize, lineHeight, letterSpacing, fontWeight, fontStyle, fontFamily (sans, serif, mono), textAlign, textTransform, whiteSpace, textOverflow, textDecorationLine, textDecorationStyle, textDecorationColor, textDecorationThickness, textUnderlineOffset, color, textGradient.
  • Background and border: backgroundColor, backgroundImage (an art key), backgroundGradient, backgroundSize, backgroundPosition, backgroundRepeat, borderRadius, borderWidth, borderStyle, borderColor, each side's borderTopWidth (and Style, Color, for Top, Right, Bottom, Left), boxShadow.
  • Effects: opacity, filter, clipPath, transform.

A gradient is a list of layers: { "linear": { "angle": 90, "stops": [{ "color": "#000", "offset": 0 }, { "color": "#fff", "offset": 1 }] } }. A shadow is a list of { offsetX, offsetY, blur?, spread?, color, inset? }. A transform is a list of { rotate }, { translateX }, { translateY } or { scale }.