Styling Dashboards
In this tutorial, we will turn a plain inventory dashboard into a report with a clear title, formatted KPIs, highlighted shortages, and a matching barchart. The first four checkpoints keep the same three products and calculations. Then we will build a stock-history view for one product, combining a line, delivery bars, a threshold, and weekend shading.
Start after Building Dashboards. Open a blank Envision script. Every code block below is a complete checkpoint: replace the whole script with the next block and run it. The previews show the output of the displayed scripts; to try an edit, run it in Envision.
Table of contents
Start with a plain inventory report
Paste this script and run it. It puts a heading, a KPI, a summary, a table, and a barchart inside one named region. The region provides a shared header and footer.
table Stock = with
[| as Sku, as OnHand, as Target, as UnitCost |]
[| "A-100", 8, 20, 10 |]
[| "B-200", 45, 15, 20 |]
[| "C-300", 12, 12, 5 |]
Stock.Value = Stock.OnHand * Stock.UnitCost
review = show region { 1..8, 1..29 } with
header "Inventory review"
footer "Example inventory"
show label "Stock and inventory value" { .., 1..3 in review }
show scalar "Inventory value" { 1..6, 5..8 in review } with sum(Stock.Value)
show summary "Units" { 7..12, 5..8 in review } with
sum(Stock.OnHand) as "On hand"
sum(Stock.Target) as "Target"
show table "Stock by SKU" { .., 10..17 in review } with
Stock.Sku
Stock.OnHand as "On hand"
Stock.Target
Stock.Value
order by Stock.Sku
show barchart "Inventory value by SKU" { .., 19..23 in review } with
sum(Stock.Value)
group by Stock.Sku
order by Stock.Sku
Check that the table contains A-100, B-200, and C-300. The inventory value is 1,040: eight units at $10, forty-five at $20, and twelve at $5. There are 65 units on hand against a total target of 47. The largest bar belongs to B-200.
The coordinates before in review are relative to the region. Empty rows
separate the tiles. The region’s height also includes its header, footer, and
padding, so leave those rows available when sizing it.
Give the report a visual hierarchy
Replace the script with this checkpoint and run it. We will give the region a pale blue background, enlarge the heading and inventory KPI, and format the inventory values as money.
table Stock = with
[| as Sku, as OnHand, as Target, as UnitCost |]
[| "A-100", 8, 20, 10 |]
[| "B-200", 45, 15, 20 |]
[| "C-300", 12, 12, 5 |]
Stock.Value = Stock.OnHand * Stock.UnitCost
accent = "blue"
review = show region { 1..8, 1..29;
tileBackground: "blue/50";
header { blockColor: #(accent) }
} with
header "Inventory review"
footer "Example inventory"
show label "Stock and inventory value" { .., 1..3 in review;
size: 1; textColor: #(accent); textAlign: left
}
show scalar "Inventory value" { 1..6, 5..8 in review;
size: 1; scalarLabelPosition: below; textAlign: left; textColor: #(accent)
} with
sum(Stock.Value) { numbers: simple; unit: "$"; precision: 2; minPrecision: 2; textColor: "blue/800" }
show summary "Units" { 7..12, 5..8 in review; numbers: simple } with
sum(Stock.OnHand) as "On hand"
sum(Stock.Target) as "Target"
show table "Stock by SKU" { .., 10..17 in review } with
Stock.Sku
Stock.OnHand as "On hand"
Stock.Target
Stock.Value { numbers: simple; unit: "$"; precision: 2; minPrecision: 2 }
order by Stock.Sku
show barchart "Inventory value by SKU" { .., 19..23 in review } with
sum(Stock.Value)
group by Stock.Sku
order by Stock.Sku
Notice the inventory KPI’s label below the value. size: 1 enlarges it, and
textAlign: left aligns it with the report’s heading. The StyleCode after
sum(Stock.Value) overrides the value’s text color without changing the label.
numbers: simple keeps the full amount visible. precision: 2 and
minPrecision: 2 display two decimal places, while unit: "$" supplies the
currency symbol. These settings change the display, leaving the calculation
unchanged.
Change accent = "blue" to accent = "orange" and run the script again.
The header, heading, and KPI label change together because #(accent) injects
that scalar value into their styles. The explicitly chosen background and KPI
value color keep their own shades. Restore blue before the next step.
Make shortages easy to find
Replace the script with this checkpoint. It adds a status column and colors the on-hand cells according to the comparison with each product’s target.
table Stock = with
[| as Sku, as OnHand, as Target, as UnitCost |]
[| "A-100", 8, 20, 10 |]
[| "B-200", 45, 15, 20 |]
[| "C-300", 12, 12, 5 |]
Stock.Value = Stock.OnHand * Stock.UnitCost
accent = "blue"
Stock.Short = Stock.OnHand < Stock.Target
Stock.Status = if Stock.Short then "Below target" else "Target met"
Stock.Color = if Stock.Short then "red" else accent
Stock.Background = if Stock.Short then "red/50" else "blue/50"
review = show region { 1..8, 1..29;
tileBackground: "blue/50";
header { blockColor: #(accent) }
} with
header "Inventory review"
footer "Example inventory"
show label "Stock and inventory value" { .., 1..3 in review;
size: 1; textColor: #(accent); textAlign: left
}
show scalar "Inventory value" { 1..6, 5..8 in review;
size: 1; scalarLabelPosition: below; textAlign: left; textColor: #(accent)
} with
sum(Stock.Value) { numbers: simple; unit: "$"; precision: 2; minPrecision: 2; textColor: "blue/800" }
show summary "Units" { 7..12, 5..8 in review; numbers: simple } with
sum(Stock.OnHand) as "On hand"
sum(Stock.Target) as "Target"
show table "Stock by SKU" { .., 10..17 in review; numbers: simple } with
Stock.Sku
Stock.OnHand as "On hand" {
columnTooltipText: "Available units, compared with the target";
value { cellBackground: #[Stock.Background]; textColor: #[Stock.Color] }
}
Stock.Target
Stock.Value { unit: "$"; precision: 2; minPrecision: 2 }
Stock.Status as "Status"
order by Stock.Sku
show barchart "Inventory value by SKU" { .., 19..23 in review } with
sum(Stock.Value)
group by Stock.Sku
order by Stock.Sku
A-100 has a red on-hand cell and the status Below target. B-200 and C-300 have blue cells and Target met. The status text carries the same message as the color. Hover over the information icon in the On hand column header to read its explanation.
#[Stock.Color] and #[Stock.Background] inject one value per product.
The nested value { ... } selector targets the cells of the on-hand column.
The header tooltip is one text shared by the column.
Change A-100’s on-hand quantity from 8 to 25 and run again. Its status changes to Target met, its cell becomes blue, and the inventory KPI increases to $1,210.00. Restore 8 before the next checkpoint.
Coordinate the chart with the table
Run this checkpoint. We will use the same product colors in the barchart and place the monetary values to the right of thinner bars.
table Stock = with
[| as Sku, as OnHand, as Target, as UnitCost |]
[| "A-100", 8, 20, 10 |]
[| "B-200", 45, 15, 20 |]
[| "C-300", 12, 12, 5 |]
Stock.Value = Stock.OnHand * Stock.UnitCost
accent = "blue"
Stock.Short = Stock.OnHand < Stock.Target
Stock.Status = if Stock.Short then "Below target" else "Target met"
Stock.Color = if Stock.Short then "red" else accent
Stock.Background = if Stock.Short then "red/50" else "blue/50"
review = show region { 1..8, 1..29;
tileBackground: "blue/50";
header { blockColor: #(accent) }
} with
header "Inventory review"
footer "Red: below target. Blue: target met."
show label "Stock and inventory value" { .., 1..3 in review;
size: 1; textColor: #(accent); textAlign: left
}
show scalar "Inventory value" { 1..6, 5..8 in review;
size: 1; scalarLabelPosition: below; textAlign: left; textColor: #(accent)
} with
sum(Stock.Value) { numbers: simple; unit: "$"; precision: 2; minPrecision: 2; textColor: "blue/800" }
show summary "Units" { 7..12, 5..8 in review; numbers: simple } with
sum(Stock.OnHand) as "On hand"
sum(Stock.Target) as "Target"
show table "Stock by SKU" { .., 10..17 in review; numbers: simple } with
Stock.Sku
Stock.OnHand as "On hand" {
columnTooltipText: "Available units, compared with the target";
value { cellBackground: #[Stock.Background]; textColor: #[Stock.Color] }
}
Stock.Target
Stock.Value { unit: "$"; precision: 2; minPrecision: 2 }
Stock.Status as "Status"
order by Stock.Sku
show barchart "Inventory value by SKU" { .., 19..23 in review;
barchartVariant: thin; barchartValuePosition: right
} with
sum(Stock.Value) { value { color: #[same(Stock.Color)]; numbers: simple; unit: "$" } }
group by Stock.Sku
order by Stock.Sku
The A-100 bar is red, matching its on-hand cell. The other bars are blue.
The footer explains that meaning. same(Stock.Color) supplies the color for
each SKU group; sum(Stock.Value) supplies its bar’s value.
Try changing A-100’s quantity to 25 once more. Its table cell and bar both turn blue, and the bar grows from $80 to $250. The inventory KPI updates at the same time. You now have one report whose formatting stays aligned with its data.
Start a stock-history view
Next, build a separate three-week history for A-100. Replace the script with
the block below and run it. This synthetic history starts with 44 units,
consumes eight units each day, and receives 56 units every Thursday.
Day.Stock records stock after that day’s demand and delivery.
start = date(2024, 4, 1)
keep span date = [start .. start + 20]
Day.t = date - start
Day.Delivery = if Day.t mod 7 == 3 then 56 else 0
Day.Demand = 8
Day.Stock = 44 + cumsum(Day.Delivery - Day.Demand) scan date
Day.Target = 20
Day.Weekend = if date >= monday(date) + 5 then 1 else 0
review = show region { 1..8, 1..22 } with
header "A-100 | Three weeks of stock"
show linechart "Stock and deliveries" { .., 1..18 in review } with
Day.Stock as "Stock"
Day.Delivery as "Deliveries"
Day.Target as "Reorder threshold"
Hover over April 4: stock is 68 units and the delivery is 56 units. Stock falls to 20 units before each replenishment. The horizontal line is a fixed reorder threshold of 20 units. For this exercise, deliveries follow a fixed schedule; the threshold is only a visual reference.
All three quantities initially use the same line presentation. We will give each one a visual role so that you can distinguish a daily stock level from an incoming delivery and a threshold.
Give each series a visual role
Replace the script with this checkpoint and run it. The data calculations are unchanged. We will add three KPIs, a navy heading, a teal stock line, pale blue delivery bars, an amber dashed threshold, and subtle weekend shading.
start = date(2024, 4, 1)
keep span date = [start .. start + 20]
Day.t = date - start
Day.Delivery = if Day.t mod 7 == 3 then 56 else 0
Day.Demand = 8
Day.Stock = 44 + cumsum(Day.Delivery - Day.Demand) scan date
Day.Target = 20
Day.Weekend = if date >= monday(date) + 5 then 1 else 0
review = show region { 1..8, 1..31;
tileBackground: "#F8FAFC!";
header { blockColor: "#17324D!"; tileBackground: "#EAF1F6!" }
} with
header "A-100 | Inventory review"
footer "Shaded columns mark weekends. All quantities are in units."
show label "See replenishments in context" { .., 1..3 in review;
size: 1; textColor: "#17324D!"; textAlign: left
}
show scalar "Lowest stock" { 1..4, 5..7 in review;
textAlign: left; textColor: "#526678!"; scalarLabelPosition: below; size: 1
} with
min(Day.Stock) { numbers: simple; textColor: "#087F8C!"; unit: " units" }
show scalar "Reorder threshold" { 5..8, 5..7 in review;
textAlign: left; textColor: "#526678!"; scalarLabelPosition: below; size: 1
} with
same(Day.Target) { numbers: simple; textColor: "#B45309!"; unit: " units" }
show scalar "Each delivery" { 9..12, 5..7 in review;
textAlign: left; textColor: "#526678!"; scalarLabelPosition: below; size: 1
} with
max(Day.Delivery) { numbers: simple; textColor: "#3563A9!"; unit: " units" }
show linechart "Daily stock and inbound deliveries" { .., 9..25 in review;
legend { legendPosition: bottom };
haxis { value { dateFormat: "MMM d" } };
vaxis { gridlineColor: "#CBD5E1!"; left { axisMin: 0; axisMax: 80 } }
} with
Day.Weekend { seriesType: background; color: "#CBD5E1!"; seriesOpacity: 0.22; seriesLegend: hidden }
Day.Delivery as "Deliveries" {
seriesType: bar; color: "#3563A9!"; seriesOpacity: 0.35; unit: " units"; seriesLegendRank: 3
}
Day.Stock as "Stock" { color: "#087F8C!"; seriesSmooth: linear; unit: " units"; seriesLegendRank: 1 }
Day.Target as "Reorder threshold" {
color: "#B45309!"; seriesPattern: dashed; seriesSmooth: linear; unit: " units"; seriesLegendRank: 2
}
Check the three KPIs: the lowest stock is 20 units, the threshold is 20 units, and each delivery contains 56 units. The stock line reaches the threshold before each delivery bar. Gray vertical bands mark Saturdays and Sundays.
The styles beside each series give it its role. seriesType: bar shows a
delivery as an amount received on one day. seriesType: background shades
the dates where Day.Weekend is nonzero; seriesLegend: hidden leaves this
calendar cue out of the legend. seriesOpacity keeps both layers light enough
to read the stock line over them. seriesPattern: dashed makes the threshold
recognizable by its pattern as well as its color.
seriesSmooth: linear connects the daily stock observations with straight
segments. The legend sits below the chart, and seriesLegendRank puts stock
first, the threshold second, and deliveries third. The vertical axis uses
fixed bounds of 0 and 80 units with pale gridlines. Hover along the chart to
read daily quantities; dateFormat: "MMM d" supplies compact month-and-day
dates in the hover readout. The colors ending in ! preserve the chosen
hexadecimal colors exactly.
Change the delivery quantity from 56 to 48 in Day.Delivery and run again.
The Each delivery KPI becomes 48 units. Stock now falls below the dashed
threshold, reaching a minimum of four units on April 17. The unchanged axis
bounds make the two runs easy to compare. Restore 56, then change the weekend
seriesOpacity from 0.22 to 0: the calendar shading disappears while the
stock line and delivery bars remain. Restore 0.22 to finish.
For a linechart with shaded ranges, continue with forecast uncertainty bands. The linechart reference links to the series, axis, and legend styling scopes.
The StyleCode reference covers selectors, injection, and color forms in more detail.