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.

User Contributed Notes
0 notes + add a note