Board Layout Implementation¶
This guide explains how Dataface's current board layout system is implemented today, why it behaves like a fixed layout system, and which design choices drive that behavior.
If you want the market/strategy companion to this doc, see the layout research report.
Related Docs¶
- Boards Overview
- Board Layouts
- Board Sizing
- Board Examples
- Layout Types Reference
- Core Architecture
- Layout research report
What This System Is¶
Today, Dataface boards are best understood as:
- a structured authored layout system
- compiled into a unified layout tree
- sized before rendering
- rendered into SVG-first output
That means the system is not doing browser-native responsive HTML layout in the way a typical React/CSS application would. It is much closer to:
- "author a board"
- "compile it into a known layout tree"
- "calculate concrete item dimensions"
- "render those dimensions into an SVG composition"
This is why the current experience feels stable and predictable across environments, but also why narrow containers often end up relying on overall scaling rather than true reflow.
The Mental Model¶
There are four major stages relevant to layout:
- YAML authoring
Authors write
rows,cols,grid, andtabsin board YAML. - Layout normalization
The compiler converts those different surface syntaxes into one internal
Layouttree. - Layout sizing The sizing pass assigns concrete width/height values to layout items.
- Layout rendering The renderer walks the sized tree and places items into SVG.
The key design choice is that layout is mostly resolved before final rendering, not delegated to the browser at the last moment.
Step 1: YAML Surface Area¶
The author-facing syntax lives in the board docs:
At this level, authors choose between:
rowscolsgridtabs
They can also nest boards recursively, attach titles/content/styles, and place charts or other boards at any level.
This YAML is intentionally simple. The complexity is pushed into normalization and sizing.
Step 2: Normalization to a Unified Layout Tree¶
Implementation entry point: dataface/core/compile/normalize_layout.py
This is one of the most important design choices in the current system.
The compiler does not keep separate downstream codepaths for raw YAML rows, cols, grid, and tabs. Instead, _build_unified_layout() converts them into a single internal Layout object with LayoutItem children.
Why this choice is good:
- renderers can trust a consistent shape
- sizing logic can run on one internal model
- nested boards work recursively without special-case YAML handling everywhere
- item references, inline charts, foreach expansion, tab metadata, and nested faces all resolve before render
This is the same pattern described in the Core Architecture doc: the compiler's job is to remove ambiguity so renderers can trust compiled types.
Step 3: Sizing Happens Before Render¶
Implementation entry point: dataface/core/compile/sizing.py
This is the second major design decision, and it explains most of the current behavior.
Dataface does data-aware sizing before layout rendering:
- KPI charts get compact default heights
- standard charts get larger default heights
- tables can use actual row counts to determine height
- markdown content is measured before final placement
- nested boards inherit a bounded space and size their children within it
This is not CSS flexbox or CSS grid. It is an explicit sizing pass that writes dimensions onto layout items before the final renderers touch them.
Important sizing rules¶
From the current implementation:
- In
rows, items generally get full available width and content-appropriate height. - In
cols, items divide available width and are normalized to a shared row height. - In
grid, positions and spans become explicit cell-based geometry. - In
tabs, only the active tab's contents are rendered in the SVG view.
The sizing module also encodes several product opinions:
- charts have natural default heights by chart type
- boards themselves do not add much implicit padding
- the root face adds page padding
- tables are special because row count affects real required height
This is a strong "known geometry first" philosophy, not an "ask the browser to figure it out later" philosophy.
Step 4: Rendering Trusts the Sized Layout¶
Implementation entry points:
dataface/core/render/renderer.pydataface/core/render/faces.pydataface/core/render/layouts.py
By the time layout renderers run, they largely trust the normalizer and sizing pass.
That shows up directly in the code:
render()callscalculate_data_aware_layout(...)before renderingrender_face_svg()computes the overall root drawing arearender_rows_layout()andrender_cols_layout()use pre-calculated item sizes- comments in
layouts.pyexplicitly say "Trust the normalizer"
This means the browser is not deciding:
- how tall a row should be
- how wide a column should be
- where a grid item belongs
- how a nested layout should reflow
Those decisions have already been made upstream.
Why It Feels Like a Fixed Layout System¶
The current design has several properties that together produce a fixed-layout feel:
1. Root dimensions are established up front¶
render_face_svg() starts with a root width and height, then subtracts page padding, title height, content height, and variable-control height to get the usable layout area.
2. Child items receive concrete geometry¶
Children are rendered with numeric widths, heights, and positions rather than semantic "grow/shrink/wrap" instructions.
3. Output is SVG-first¶
The HTML path wraps the SVG output rather than re-implementing the layout as native HTML/CSS layout. That preserves consistency, but it also means the authored composition remains the primary truth.
4. Layout adaptation is mostly compile/render logic, not browser reflow¶
If the surrounding viewport changes, the browser is not re-computing a fresh semantic layout tree. It is mostly displaying the already-composed result.
Put differently: the system is optimized for deterministic composition, not continuous responsive reflow.
Why It Was Built This Way¶
There are good reasons for the current design.
Predictability¶
The same authored board produces a stable composition. That is valuable for:
- visual QA
- screenshots and exports
- examples and documentation
- dashboard-builder mental model
- "single screen" dashboard design
Unified pipeline¶
The same compiled structure can feed:
- SVG
- HTML wrapper output
- PNG/PDF export
- terminal rendering via parallel layout concepts
Content-aware sizing¶
Pure browser layout would not automatically know that:
- KPI cards can be short
- markdown content needs measured height
- tables need row-count-aware height
The sizing pass lets Dataface encode dashboard-specific heuristics directly.
Recursive nesting¶
Nested boards become much easier to reason about when each level receives a concrete box and sizes its children within that box.
Tradeoffs of the Current Design¶
The same choices that make the system predictable also create the behavior behind the current research problem.
Benefits¶
- stable authored layouts
- easy to reason about composition
- strong support for at-a-glance dashboards
- consistent export behavior
- deterministic recursive layout
Costs¶
- limited browser-native responsiveness
- narrow containers often depend on whole-layout scaling
- text legibility problems show up before composition breaks
- embeds and split-pane environments are harder
- different content types cannot adapt independently unless explicitly implemented
This is exactly why the companion layout research report recommends a hybrid evolution rather than a full replacement.
Current Design Choices, Explained¶
Choice: one unified internal layout model¶
Why:
- simpler render pipeline
- fewer downstream special cases
- recursive nesting works cleanly
Consequence:
- layout behavior is centralized and easier to evolve
Choice: render-time sizing, not compile-time-only sizing¶
Why:
- some content, especially tables and markdown, needs real measurements
Consequence:
- output geometry can reflect data volume
- layout is still explicit and deterministic once calculated
Choice: SVG-first rendering¶
Why:
- consistent cross-format output
- chart rendering already targets SVG well
- easier export story
Consequence:
- composition remains stable
- browser-native responsive layout is not the default runtime behavior
Choice: root page padding, minimal board padding¶
Why:
- keep board composition dense and dashboard-like
Consequence:
- good information density
- less slack space for adaptation in constrained containers
Choice: rows and cols encode simple strong defaults¶
Why:
- authors can express dashboards quickly
- layout rules stay understandable
Consequence:
- the system is approachable
- more advanced responsive behavior needs explicit future work rather than emerging naturally from HTML flow
Where to Read the Code¶
If you want to trace the current implementation directly, start here:
dataface/core/compile/normalize_layout.pydataface/core/compile/sizing.pydataface/core/render/renderer.pydataface/core/render/faces.pydataface/core/render/layouts.pytests/core/test_layout_rendering.py
The Core Architecture doc is the best broader map of how those files fit together.
How This Connects to the Research Doc¶
This implementation guide explains what we built and why it behaves the way it does today.
The companion layout research report explains how the broader industry talks about these models, what other BI tools do, and what Dataface should probably do next.
They are intentionally paired:
- this doc: current state, implementation, design rationale
- research doc: terminology, market comparison, strategic recommendation
Bottom Line¶
Dataface's current board system is a compiled, sized, SVG-first layout engine with strong deterministic behavior. That is why it works well for stable, single-screen dashboards and export-friendly compositions.
It is also why it behaves more like a fixed canvas with calculated geometry than a browser-native responsive layout system.
That is not an accident or a bug in the implementation. It is the direct result of deliberate design choices in:
- layout normalization
- data-aware sizing
- SVG-first rendering
- recursive explicit geometry
If we want more responsive behavior, the right next step is not to pretend the current system is already responsive. The right next step is to evolve this architecture deliberately, with the tradeoffs in the companion research doc in mind.