Layout#
Nelsie uses a layout system that is based on the Flexbox and Grid system and adds some extra features.
The central element of the Nelsie layout system is the Box. A box is a rectangular area on a slide that has a position and size. A box can contain other boxes or content (a text or an image). Each Nelsie slide contains a hierarchical tree of boxes. Boxes usually do not produce visual output directly, but they dictate how their children are arranged on a slide.
Creating a box#
A new box is created by calling the .box()
method on an existing box or a slide. This will return a new box that will be a child of the element on which you call the box method.
This is a s minimal example where we create a box inside a slide:
When we render this slide, we see an empty slide because the box itself produces no visual output.
To make them visible, in the next example we set a background color for the boxes. We also set them to a fixed size, because by default box tries to be as small as possible and a box with no content takes up zero space.
Example where we create three boxes:
@deck.slide()
def three_boxes(slide):
slide.box(width=600, height=200, bg_color="red")
slide.box(width=600, height=200, bg_color="green")
slide.box(width=600, height=200, bg_color="blue")
The full reference on .box()
parameters is in the section Box
Debugging layout#
Another way how to visualize boxes is to set debug_layout
to True
in the slide configuration:
@deck.slide(debug_layout=True)
def debug_boxes(slide):
slide.box(width=600, height=200)
slide.box(width=600, height=200)
slide.box(width=600, height=200)
For more configuration for debugging layout, see Debugging layout.
Box main axis#
Boxes can have either be vertical or horizontal main axis:
-
Vertical boxes place its child items vertically in a column. Their main axis is vertical and their cross axis is horizontal.
-
Horizontal boxes place its child items horizontally in a row. Their main axis is horizontal and their cross axis is vertical.
By default, box is vertical. It can be changed by setting the parameter row
to True
:
@deck.slide()
def three_boxes(slide):
my_box = slide.box(row=True)
my_box.box(width=200, height=200, bg_color="red")
my_box.box(width=200, height=200, bg_color="green")
my_box.box(width=200, height=200, bg_color="blue")
A box in a box
Box can contain other boxes. A box within the box can be created by calling the .box()
method on the parent box. In this example, the slide's root box contains my_box
and my_box
contains three other boxes.
You can change the reverse order of child boxes by setting reverse=True
. It will arrange elements from left to right (or bottom to top if row=True
).
@deck.slide()
def three_boxes(slide):
my_box = slide.box(reverse=True)
my_box.box(width=200, height=200, bg_color="red")
my_box.box(width=200, height=200, bg_color="green")
my_box.box(width=200, height=200, bg_color="blue")
Box size#
Each box has a width and a height. By default, the box tries to take up as little space as possible. It will wraps its content tightly. If there is no content, the box has zero size. This behaviour can be configured by setting the width
, height
, flex-grow
and flex-shrink
parameters.
Width and Height#
Weight/height parameters:
None
- (default) Automatic size. Minimum size around the content ifflex-grow
/flex-shrink
is not set.int
orfloat
orstr
containing digits -- A fixed size given in pixels (example values:20.5
, or"1.5"
)str
in format"XX%"
whereXX
is an integer -- A relative size to the parent box, in percent (example:"50%"
)LayoutExpr
- A fixed size defined by a layout expression.
Flex grow#
The flex_grow
parameter takes a float
value. The default is 0
. This attribute specifies how much of the remaining space of its parent box should be allocated to this box.
The remaining space is the size of the box minus the size of all its children. If multiple sibling boxes have positive `flex_grow' values, it is distributed according to the ratio defined by their values.
This property is equivalent to the CSS property `flex-grow'.
@deck.slide()
def flex_grow_demo(slide):
slide.box(width=200, height=100, bg_color="red")
slide.box(width=200, flex_grow=1, bg_color="green")
slide.box(width=200, height=200, bg_color="blue")
Padding & Margin#
Padding (inner space) and margin (outer space) can be set via p_left
, p_right
, p_top
, and p_bottom
for setting padding and m_left
, m_right
, m_top
, and m_bottom
for setting a margin.
@deck.slide()
def flex_grow_demo(slide):
my_box = slide.box(p_top=100, p_left=50, bg_color="red")
my_box.box(width=200, height=200, bg_color="green")
There are also the following parameters for setting more padding/margin parameters at once:
p_x
that setsp_left
andp_right
p_y
that setsp_top
andp_bottom
m_x
that setsm_left
andm_right
m_y
that setsm_top
andm_bottom
Arranging box children#
Nelsie provides a flexbox layout system See Flexbox froggy for a nice tutorial.
Most of the layouts can be done via flexbox; however, also supports grid layout, see Grid layout.
Nelsie supports from flexbox: justify_content
, align_items
, align_self
, align_items
, align_self
, justify_self
, align_content
, justify_content
and gap
.
The default configuration is "center"
for configurations justify_content
and align_items
, i.e. items are put in the center on both axes.
Example for justify_content
#
@deck.slide()
def justify_content_start(slide):
b = slide.box(height="100%", justify_content="start")
b.box(width=200, height=150, bg_color="red")
b.box(width=200, height=150, bg_color="green")
b.box(width=200, height=150, bg_color="blue")
@deck.slide()
def justify_content_end(slide):
b = slide.box(height="100%", justify_content="end")
b.box(width=200, height=150, bg_color="red")
b.box(width=200, height=150, bg_color="green")
b.box(width=200, height=150, bg_color="blue")
@deck.slide()
def justify_content_end(slide):
b = slide.box(height="100%", justify_content="space-evenly")
b.box(width=200, height=150, bg_color="red")
b.box(width=200, height=150, bg_color="green")
b.box(width=200, height=150, bg_color="blue")
Fixed positioning of a box#
You can set parameters x
and y
to set a fix position of the box independantly on the layout engine.
None
- (default) Coordianes are set by the layout engine.int
orfloat
orstr
containing digits -- A fixed position given relative to the parent box in pixels (example values:20.5
, or"1.5"
)str
in format"XX%"
whereXX
is an integer -- A fixed position relative to the parent box, in percent (example value:"50%"
means thatx
(resp.y
) is set to the 50% of width (resp. height) of the parent box)LayoutExpr
- A fixed position defined by a layout expression.
Grid layout#
Nelsie also supports grid layout system; see Grid garden for a nice tutorial.
@deck.slide()
def justify_content_start(slide):
b = slide.box(
width="100%", height="100%",
grid_template_columns=("1 fr", "1fr"),
grid_template_rows=("1 fr", "1fr"))
b.box(grid_column=2, grid_row=2, bg_color="orange").text("Grid 2-2")
Grid templates (grid_template_rows
and grid_template_columns
) may have values as follows:
200
or"200"
- size of row/column in pixels"50%"
- size of row/column in percents"1 fr"
- size of row/column in fractions
Grid positions (grid_row
and grid_column
) may have values as follows:
2
- box at row/column2
(2, 5)
- box that spans from row/column 2 to row/column 5(2, "span 3")
- box that spans from row/column 2 over 3 row/column.
A rich table example#
@deck.slide()
def grid_demo(slide):
data = [
("Name", "Time", "Type"),
("Jane", 3.5, "A1"),
("John", 4.1, "B7"),
("Johanna", 12.0, "C1"),
("Elise", 12.5, "D4"),
("Max", 320.2, "E1")
]
# Draw the table
table = slide.box(
width="70%",
grid_template_columns=["2fr", "1fr", 130],
grid_template_rows=[50] + [40] * (len(data) - 1),
bg_color="#ddd",
)
header_style = TextStyle(weight=800)
table.box(grid_column=(1, 4), grid_row=1, bg_color="#fbc")
for i in range(2, len(data) + 1, 2):
table.box(grid_column=(1, 4), grid_row=i, bg_color="#eee")
column1 = table.box(grid_column=2, grid_row=(1, len(data) + 1))
stroke = Stroke(color="#888", width=2)
column1.draw(Path(stroke=stroke).move_to(0, 0).line_to(0, "100%"))
column1.draw(Path(stroke=stroke).move_to("100%", 0).line_to("100%", "100%"))
# Fill the table with data
for i, row in enumerate(data, 1):
s = header_style if i == 1 else None
table.box(grid_column=1, grid_row=i).text(row[0], s)
table.box(grid_column=2, grid_row=i, row=True, justify_content="end", m_right=30).text(str(row[1]), s)
table.box(grid_column=3, grid_row=i, row=True, justify_content="start", m_left=30).text(row[2], s)
Method .overlay()
#
There is a .overlay()
method that is a shortcut for .box(x=0, y=0, width="100%", height="100%")
; it creates a box that spans over the whole parent box.