16  Configuration

Napkinsketch has three kinds of options:

This chapter covers configuration and plot options.

(ns napkinsketch-book.configuration
  (:require
   ;; Shared datasets for these docs
   [napkinsketch-book.datasets :as data]
   ;; Kindly β€” notebook rendering protocol
   [scicloj.kindly.v4.kind :as kind]
   ;; Napkinsketch β€” composable plotting
   [scicloj.napkinsketch.api :as sk]))

We use the iris dataset throughout.

We define base-plot as a function because sketches render at display time β€” calling the function produces a fresh specification that picks up the current configuration.

(defn base-plot
  [] (-> data/iris
         (sk/lay-point :sepal_length :sepal_width {:color :species})))

Inspecting the Current Configuration

sk/config returns the resolved configuration as a plain map. It merges all active configuration layers into one map.

(sk/config)
{:margin-multi 30,
 :validate true,
 :point-stroke "none",
 :title-offset 18,
 :tick-spacing-y 40,
 :panel-size 200,
 :label-offset 18,
 :label-font-size 13,
 :default-color "#333",
 :width 600,
 :point-stroke-width 0,
 :annotation-dash [4 3],
 :legend-width 100,
 :theme {:bg "#E8E8E8", :grid "#F5F5F5", :font-size 11},
 :bin-method :sturges,
 :domain-padding 0.05,
 :strip-height 16,
 :point-opacity 0.75,
 :line-width 2.5,
 :grid-stroke-width 0.6,
 :tick-spacing-x 60,
 :strip-font-size 10,
 :title-font-size 15,
 :band-opacity 0.15,
 :bar-opacity 0.85,
 :annotation-stroke "#333",
 :height 400,
 :margin 30,
 :point-radius 3.0}

Configuration Keys

Each key, its default value, and a description.

(def category-order
  ["Layout" "Theme" "Typography" "Points" "Bars & Lines"
   "Annotations" "Ticks" "Statistics" "Labels" "Behavior"
   "Color" "Interaction" "Output"])
(kind/table
 {:column-names ["Key" "Default" "Category" "Description"]
  :row-maps
  (let [cfg (sk/config)]
    (->> sk/config-key-docs
         (sort-by (fn [[k [cat]]]
                    [(.indexOf ^java.util.List category-order cat) (name k)]))
         (mapv (fn [[k [cat desc]]]
                 {"Key" (kind/code (pr-str k))
                  "Default" (kind/code (pr-str (get cfg k)))
                  "Category" cat
                  "Description" desc}))))})
Key Default Category Description
:height
400
Layout Plot height in pixels
:legend-position
nil
Layout Legend placement β€” :right, :bottom, :top, or :none
:legend-width
100
Layout Width reserved for the legend column
:margin
30
Layout Margin around single-panel plots (pixels)
:margin-multi
30
Layout Margin around multi-panel plots (pixels)
:panel-size
200
Layout Default panel size for faceted/multi-variable grids
:width
600
Layout Plot width in pixels
:theme
{:bg "#E8E8E8", :grid "#F5F5F5", :font-size 11}
Theme Nested map {:bg :grid :font-size} β€” visual identity
:label-font-size
13
Typography Font size for axis labels
:strip-font-size
10
Typography Font size for facet strip labels
:title-font-size
15
Typography Font size for the plot title
:point-opacity
0.75
Points Default point opacity (0.0–1.0)
:point-radius
3.0
Points Default point radius
:point-stroke
"none"
Points Point border stroke color ("none" to disable)
:point-stroke-width
0
Points Point border stroke width
:bar-opacity
0.85
Bars & Lines Default bar fill opacity
:grid-stroke-width
0.6
Bars & Lines Grid line stroke width
:line-width
2.5
Bars & Lines Default line stroke width
:annotation-dash
[4 3]
Annotations Dash pattern [dash gap] for annotation lines
:annotation-stroke
"#333"
Annotations Stroke color for annotation marks
:band-opacity
0.15
Annotations Opacity for confidence bands
:tick-spacing-x
60
Ticks Minimum pixel spacing between x-axis ticks
:tick-spacing-y
40
Ticks Minimum pixel spacing between y-axis ticks
:bin-method
:sturges
Statistics Histogram bin count method (:sturges, :sqrt, :rice, :fd)
:domain-padding
0.05
Statistics Fractional padding added to numeric domains
:label-offset
18
Labels Pixel offset for axis labels from the axis
:strip-height
16
Labels Height of facet strip label bars
:title-offset
18
Labels Pixel offset for the title from the top
:default-color
"#333"
Behavior Fallback color when no color mapping is set
:validate
true
Behavior When true, validate plans against Malli schema
:color-midpoint
nil
Color Center value for diverging color scales
:color-scale
nil
Color Continuous color scale β€” :sequential, :diverging, or keyword
:palette
nil
Color Categorical palette β€” keyword, vector, or map
:brush
nil
Interaction Enable drag-to-select brush (truthy value)
:tooltip
nil
Interaction Enable hover tooltips (truthy value)
:format
nil
Output Render format β€” :svg (default)

Plot Options

These options are accepted by sk/options, sk/plan, and sk/plot but are inherently per-plot β€” text content or nested config override.

(kind/table
 {:column-names ["Key" "Category" "Description"]
  :row-maps
  (->> sk/plot-option-docs
       (sort-by (fn [[k [cat]]] [cat (name k)]))
       (mapv (fn [[k [cat desc]]]
               {"Key" (kind/code (pr-str k))
                "Category" cat
                "Description" desc})))})
Key Category Description
:config
Config Nested config map merged into resolved config
:caption
Content Plot caption string (bottom)
:subtitle
Content Plot subtitle string
:title
Content Plot title string
:x-label
Content X-axis label (overrides inferred)
:y-label
Content Y-axis label (overrides inferred)

Using Plot Options

Pass an options map to sk/options to override any setting for a single plot. Plot options have the highest priority β€” they override everything else.

Custom dimensions β€” the defaults are:

(select-keys (sk/config) [:width :height])
{:width 600, :height 400}
(-> (base-plot)
    (sk/options {:width 900 :height 250}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.24.44.64.85.05.25.45.65.86.06.26.46.66.87.07.27.47.67.88.02.02.53.03.54.04.5

Theme deep-merge β€” only the specified keys change. Here we set a white background; :grid and :font-size keep their defaults:

(-> (base-plot)
    (sk/options {:theme {:bg "#FFFFFF"}}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Named palette:

(-> (base-plot)
    (sk/options {:palette :dark2}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Global Overrides with set-config!

sk/set-config! sets overrides that persist across calls β€” useful when you want a consistent style for an entire session or notebook.

Set a global width override and render:

(sk/set-config! {:width 800})
{:width 800}
(select-keys (sk/config) [:width :height])
{:width 800, :height 400}
(-> (base-plot))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Reset to library defaults by passing nil:

(sk/set-config! nil)
nil
(select-keys (sk/config) [:width :height])
{:width 600, :height 400}

Thread-Local Overrides with with-config

sk/with-config is a macro that binds configuration overrides for the duration of its body, then automatically reverts. This is ideal for one-off experiments or when different sections of a notebook need different settings.

A dark theme, scoped to this block:

(sk/with-config {:theme {:bg "#1a1a2e" :grid "#16213e" :font-size 8}}
  (-> (base-plot)
      (sk/options {:title "Dark Theme via with-config"})))
Dark Theme via with-configsepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Partial theme override β€” only :bg changes; :grid and :font-size are deep-merged from the defaults:

(sk/with-config {:theme {:bg "#F5F5DC"}}
  (-> (base-plot)
      (sk/options {:title "Partial Theme Override"})))
Partial Theme Overridesepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Outside the body, the default theme is back:

(select-keys (sk/config) [:width :height])
{:width 600, :height 400}

The Precedence Chain

When multiple configuration layers are active, the highest-priority layer wins for each key. The chain from highest to lowest:

plot options  >  with-config  >  set-config!  >  napkinsketch.edn  >  library defaults

Let’s demonstrate all three programmatic levels at once, using a different key at each level so we can see each one win.

Before overriding, the library default for point-radius is:

(:point-radius (sk/config))
3.0

Now set a global override for width, height, and point-radius:

(sk/set-config! {:width 800 :height 350 :point-radius 5.0})
{:width 800, :height 350, :point-radius 5.0}

Layer a thread-local override on top for width and height (but not point-radius):

(def precedence-result
  (sk/with-config {:width 1200 :height 500}
    ;; Pass plot options for width only:
    (let [plan (sk/plan (base-plot) {:width 900})]
      {:plan-width (:width plan)
       :plan-height (:height plan)})))
precedence-result
{:plan-width 900, :plan-height 500}

We can verify point-radius too β€” only set-config! touched it, so it wins over the library default (2.5):

(def precedence-point-radius
  (sk/with-config {:width 1200 :height 500}
    (:point-radius (sk/config))))
precedence-point-radius
5.0

The rendered plot reflects the same precedence:

(def precedence-plot
  (sk/with-config {:width 1200 :height 500}
    (-> (base-plot)
        (sk/options {:width 900}))))
precedence-plot
sepal widthsepal lengthspeciessetosaversicolorvirginica4.24.44.64.85.05.25.45.65.86.06.26.46.66.87.07.27.47.67.88.02.02.22.42.62.83.03.23.43.63.84.04.24.4

Clean up the global override.

(sk/set-config! nil)
nil
(select-keys (sk/config) [:width :height :point-radius])
{:width 600, :height 400, :point-radius 3.0}

To summarize what happened:

Key Library default set-config! with-config plot options Winner
:width 600 800 1200 900 900 (plot options)
:height 400 350 500 β€” 500 (with-config)
:point-radius 2.5 5.0 β€” β€” 5.0 (set-config!)

Project-Level Defaults with napkinsketch.edn

For team-wide consistency, create a napkinsketch.edn file in your project root (or anywhere on the classpath). It is read automatically with a 1-second cache.

Example napkinsketch.edn:

{:width 800
 :height 500
 :theme {:bg "#FFFFFF" :grid "#F0F0F0" :font-size 10}
 :palette :tableau10
 :point-radius 3}

This layer sits between library defaults and set-config! in the precedence chain β€” it overrides defaults but is overridden by any programmatic configuration.

Theme Customization

The :theme key is a nested map with three entries:

  • :bg β€” panel background color (hex string)
  • :grid β€” grid line color (hex string)
  • :font-size β€” tick label font size (number)
(count (:theme (sk/config)))
3

All configuration merging uses deep-merge β€” nested maps like :theme are merged recursively at every level (sk/options, sk/with-config, sk/set-config!, and napkinsketch.edn). You only need to specify the keys you want to change.

Override only :bg, keep default :grid and :font-size:

(-> (base-plot)
    (sk/options {:theme {:bg "#F5F5DC"}})
    sk/plot)
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Override all three for a dark theme:

(-> (base-plot)
    (sk/options {:title "Full Dark Theme"
                 :theme {:bg "#2d2d2d" :grid "#444444" :font-size 10}})
    sk/plot)
Full Dark Themesepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Comparing Two Themes Side by Side

sk/arrange composes independent plots in a CSS grid. Each plot can have its own theme.

(sk/arrange
 [(-> (base-plot)
      (sk/options {:title "Light"
                   :theme {:bg "#FFFFFF" :grid "#EEEEEE" :font-size 8}
                   :width 350 :height 250}))
  (-> (base-plot)
      (sk/options {:title "Dark"
                   :theme {:bg "#2d2d2d" :grid "#444444" :font-size 8}
                   :width 350 :height 250}))])
Lightsepal widthsepal lengthspeciessetosaversicolorvirginica56782.02.53.03.54.04.5Darksepal widthsepal lengthspeciessetosaversicolorvirginica56782.02.53.03.54.04.5

Palette Configuration

The :palette key controls the color cycle for categorical color mappings. It accepts:

  • a keyword β€” any palette name from the clojure2d color library (hundreds available: ColorBrewer, Wes Anderson, thi.ng, paletteer, etc.)

  • a vector of hex strings: ["#E74C3C" "#3498DB" "#2ECC71"]

  • a map of {category-value "#hex"} for explicit assignment

Common palette names: :set1, :set2, :dark2, :tableau10, :category10, :pastel1, :accent, :paired. Use (clojure2d.color/find-palette #"pattern") to discover more.

Palette works at every configuration level.

Named palette via plot options:

(-> (base-plot)
    (sk/options {:palette :tableau10}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Custom vector palette:

(-> (base-plot)
    (sk/options {:palette ["#E74C3C" "#3498DB" "#2ECC71"]}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Explicit map palette:

(-> (base-plot)
    (sk/options {:palette {"setosa" "#FF6B6B"
                           "versicolor" "#4ECDC4"
                           "virginica" "#45B7D1"}}))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Global palette via set-config!:

(sk/set-config! {:palette :pastel1})
{:palette :pastel1}
(-> (base-plot))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5
(sk/set-config! nil)
nil

Thread-local palette via with-config:

(sk/with-config {:palette :accent}
  (-> (base-plot)))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

Color Scale Configuration

When a numeric column is mapped to :color, Napkinsketch uses a continuous gradient (dark-to-light blue by default). The :color-scale option controls which gradient is used.

Default continuous color (dark blue to light blue):

(-> {:x (range 50) :y (range 50) :c (range 50)}
    (sk/lay-point :x :y {:color :c}))
yxc0.00049.000510152025303540455005101520253035404550

Color scale override via plot options β€” inferno gradient:

(-> {:x (range 50) :y (range 50) :c (range 50)}
    (sk/lay-point :x :y {:color :c})
    (sk/options {:color-scale :inferno}))
yxc0.00049.000510152025303540455005101520253035404550

Thread-local color scale via with-config:

(sk/with-config {:color-scale :plasma}
  (-> {:x (range 50) :y (range 50) :c (range 50)}
      (sk/lay-point :x :y {:color :c})))
yxc0.00049.000510152025303540455005101520253035404550

The plan records :color-scale in its legend. The renderer uses the pre-computed gradient stops, or resolves a fresh gradient if the render-time configuration specifies a different color scale.

(-> {:x (range 50) :y (range 50) :c (range 50)}
    (sk/lay-point :x :y {:color :c})
    (sk/plan {:color-scale :inferno})
    :legend
    (select-keys [:color-scale :type]))
{:color-scale :inferno, :type :continuous}

Validation Control

By default, sk/plan validates the output against a Malli schema and throws if the plan is malformed. This is controlled by the :validate key.

Two helper functions let you inspect plans manually:

  • sk/valid-plan? β€” returns true or false
  • sk/explain-plan β€” returns nil if valid, or a Malli explanation map

Default behavior (validate = true) β€” a valid plan passes silently:

(sk/plan (base-plot))
{:panels
 [{:coord :cartesian,
   :y-domain [1.88 4.5200000000000005],
   :x-scale {:type :linear},
   :x-domain [4.12 8.08],
   :x-ticks
   {:values [4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0],
    :labels ["4.5" "5.0" "5.5" "6.0" "6.5" "7.0" "7.5" "8.0"],
    :categorical? false},
   :col 0,
   :layers
   [{:mark :point,
     :style {:opacity 0.75, :radius 3.0},
     :groups
     [{:color
       [0.8941176470588236
        0.10196078431372549
        0.10980392156862745
        1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
       :label "setosa",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}
      {:color
       [0.21568627450980393
        0.49411764705882355
        0.7215686274509804
        1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[7.000, 6.400, 6.900, 5.500, 6.500, 5.700, 6.300, 4.900, 6.600, 5.200, 5.000, 5.900, 6.000, 6.100, 5.600, 6.700, 5.600, 5.800, 6.200, 5.600...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.200, 3.200, 3.100, 2.300, 2.800, 2.800, 3.300, 2.400, 2.900, 2.700, 2.000, 3.000, 2.200, 2.900, 2.900, 3.100, 3.000, 2.700, 2.200, 2.500...],
       :label "versicolor",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69...]}
      {:color
       [0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[6.300, 5.800, 7.100, 6.300, 6.500, 7.600, 4.900, 7.300, 6.700, 7.200, 6.500, 6.400, 6.800, 5.700, 5.800, 6.400, 6.500, 7.700, 7.700, 6.000...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.300, 2.700, 3.000, 2.900, 3.000, 3.000, 2.500, 2.900, 2.500, 3.600, 3.200, 2.700, 3.000, 2.500, 2.800, 3.200, 3.000, 3.800, 2.600, 2.200...],
       :label "virginica",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119...]}],
     :y-domain [2.0 4.4],
     :x-domain [4.3 7.9]}],
   :y-scale {:type :linear},
   :y-ticks
   {:values [2.0 2.5 3.0 3.5 4.0 4.5],
    :labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
    :categorical? false},
   :row 0}],
 :width 600,
 :height 400,
 :caption nil,
 :total-width 722.5,
 :legend-position :right,
 :layout-type :single,
 :layout
 {:subtitle-pad 0,
  :legend-w 100,
  :caption-pad 0,
  :y-label-pad 22.5,
  :legend-h 0,
  :title-pad 0,
  :strip-h 0,
  :x-label-pad 18,
  :strip-w 0},
 :grid {:rows 1, :cols 1},
 :legend
 {:title :species,
  :entries
  [{:label "setosa",
    :color
    [0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0]}
   {:label "versicolor",
    :color
    [0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0]}
   {:label "virginica",
    :color
    [0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0]}]},
 :panel-height 400.0,
 :title nil,
 :y-label "sepal width",
 :alpha-legend nil,
 :x-label "sepal length",
 :subtitle nil,
 :panel-width 600.0,
 :size-legend nil,
 :total-height 418.0,
 :margin 30}

The rendered plot works normally:

(-> (base-plot))
sepal widthsepal lengthspeciessetosaversicolorvirginica4.55.05.56.06.57.07.58.02.02.53.03.54.04.5

What Validation Catches

To see what happens when a plan is malformed, we can build one with validation disabled, then corrupt it. First, create a valid plan and confirm it passes:

(def good-plan (sk/plan (base-plot) {:validate false}))
(sk/valid-plan? good-plan)
true

Now corrupt the :width to a string β€” this violates the schema, which requires a positive integer:

(def bad-plan (assoc good-plan :width "not-a-number"))
(sk/valid-plan? bad-plan)
false

sk/explain-plan pinpoints the problem. The :errors key in the returned map shows exactly which path failed and why:

(-> (sk/explain-plan bad-plan)
    :errors
    first
    (select-keys [:path :in :value]))
{:path [:width], :in [:width], :value "not-a-number"}

With validation enabled (the default), sk/plan would throw an exception for such a malformed plan. We can verify this by catching the exception:

(try
  (let [plan (sk/plan (base-plot) {:validate false})
        bad (assoc plan :width "not-a-number")]
    (when-let [explanation (sk/explain-plan bad)]
      (throw (ex-info "Plan does not conform to schema"
                      {:explanation explanation})))
    :no-error)
  (catch Exception e
    {:caught true
     :message (.getMessage e)}))
{:caught true, :message "Plan does not conform to schema"}

Disabling Validation

Disable validation with :validate false:

(sk/plan (base-plot) {:validate false})
{:panels
 [{:coord :cartesian,
   :y-domain [1.88 4.5200000000000005],
   :x-scale {:type :linear},
   :x-domain [4.12 8.08],
   :x-ticks
   {:values [4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0],
    :labels ["4.5" "5.0" "5.5" "6.0" "6.5" "7.0" "7.5" "8.0"],
    :categorical? false},
   :col 0,
   :layers
   [{:mark :point,
     :style {:opacity 0.75, :radius 3.0},
     :groups
     [{:color
       [0.8941176470588236
        0.10196078431372549
        0.10980392156862745
        1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[5.100, 4.900, 4.700, 4.600, 5.000, 5.400, 4.600, 5.000, 4.400, 4.900, 5.400, 4.800, 4.800, 4.300, 5.800, 5.700, 5.400, 5.100, 5.700, 5.100...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.500, 3.000, 3.200, 3.100, 3.600, 3.900, 3.400, 3.400, 2.900, 3.100, 3.700, 3.400, 3.000, 3.000, 4.000, 4.400, 3.900, 3.500, 3.800, 3.800...],
       :label "setosa",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...]}
      {:color
       [0.21568627450980393
        0.49411764705882355
        0.7215686274509804
        1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[7.000, 6.400, 6.900, 5.500, 6.500, 5.700, 6.300, 4.900, 6.600, 5.200, 5.000, 5.900, 6.000, 6.100, 5.600, 6.700, 5.600, 5.800, 6.200, 5.600...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.200, 3.200, 3.100, 2.300, 2.800, 2.800, 3.300, 2.400, 2.900, 2.700, 2.000, 3.000, 2.200, 2.900, 2.900, 3.100, 3.000, 2.700, 2.200, 2.500...],
       :label "versicolor",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69...]}
      {:color
       [0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
       :xs #tech.v3.dataset.column<float64>[50]
:sepal_length
[6.300, 5.800, 7.100, 6.300, 6.500, 7.600, 4.900, 7.300, 6.700, 7.200, 6.500, 6.400, 6.800, 5.700, 5.800, 6.400, 6.500, 7.700, 7.700, 6.000...],
       :ys #tech.v3.dataset.column<float64>[50]
:sepal_width
[3.300, 2.700, 3.000, 2.900, 3.000, 3.000, 2.500, 2.900, 2.500, 3.600, 3.200, 2.700, 3.000, 2.500, 2.800, 3.200, 3.000, 3.800, 2.600, 2.200...],
       :label "virginica",
       :row-indices #tech.v3.dataset.column<int64>[50]
:__row-idx
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119...]}],
     :y-domain [2.0 4.4],
     :x-domain [4.3 7.9]}],
   :y-scale {:type :linear},
   :y-ticks
   {:values [2.0 2.5 3.0 3.5 4.0 4.5],
    :labels ["2.0" "2.5" "3.0" "3.5" "4.0" "4.5"],
    :categorical? false},
   :row 0}],
 :width 600,
 :height 400,
 :caption nil,
 :total-width 722.5,
 :legend-position :right,
 :layout-type :single,
 :layout
 {:subtitle-pad 0,
  :legend-w 100,
  :caption-pad 0,
  :y-label-pad 22.5,
  :legend-h 0,
  :title-pad 0,
  :strip-h 0,
  :x-label-pad 18,
  :strip-w 0},
 :grid {:rows 1, :cols 1},
 :legend
 {:title :species,
  :entries
  [{:label "setosa",
    :color
    [0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0]}
   {:label "versicolor",
    :color
    [0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0]}
   {:label "virginica",
    :color
    [0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0]}]},
 :panel-height 400.0,
 :title nil,
 :y-label "sepal width",
 :alpha-legend nil,
 :x-label "sepal length",
 :subtitle nil,
 :panel-width 600.0,
 :size-legend nil,
 :total-height 418.0,
 :margin 30}

You can also disable validation globally for a debugging session:

(sk/set-config! {:validate false})
;; ... work freely ...
(sk/set-config! nil)  ;; re-enable

Summary

Mechanism Scope Persistence Example
plot options single call none (sk/options {...}) or (sk/plot views {...})
with-config lexical body until body exits (sk/with-config {:width 800} ...)
set-config! global until reset (sk/set-config! {:width 800})
napkinsketch.edn project file on disk {:width 800} in project root
library defaults everywhere built-in resources/napkinsketch-defaults.edn

Precedence: plot options > with-config > set-config! > napkinsketch.edn > library defaults.

Use sk/config at any time to see the resolved configuration.

What’s Next

  • Customization β€” annotations, color scales, tooltips, and brush selection
  • Faceting β€” split plots into panels by category
source: notebooks/napkinsketch_book/configuration.clj