Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Layout

Nifty arranges your UI with two containers:

  • column stacks children top to bottom.
  • row places children left to right.

Put rows inside columns (and columns inside rows) to build any layout.

column(spacing: 12, padding: 20) {
  label("Title", style: .title)
  row(spacing: 8) {
    button("Cancel") { }
    button("OK", style: .primary) { }
  }
}

Spacing and alignment

Both column and row take the same optional settings. These four place their children:

SettingWhat it doesExample
spacingspace between children, in pointsspacing: 8
paddingspace around the edge, in pointspadding: 20
alignwhere children sit across the stackalign: .center
justifywhere children sit along the stackjustify: .space_between

Numbers must be between 0 and 10000.

align and justify, explained

The easy way to remember: justify works in the direction the stack flows; align works the other way.

justify moves things…align moves things…
columnup and downleft and right
rowleft and rightup and down

align options:

OptionChildren…
.stretch (default)stretch to fill the width of a column (or height of a row)
.startsit at the left of a column (or top of a row)
.centersit in the middle
.endsit at the right of a column (or bottom of a row)

justify options:

OptionChildren…
.start (default)bunch up at the start
.centerbunch up in the middle
.endbunch up at the end
.space_betweenspread out, with the first and last at the edges

Pushing things apart with spacer

spacer() takes up all the leftover space. Use it to push things to opposite ends:

row() {
  label("Todos", style: .title)
  spacer()
  label("3 left", style: .caption)
}

The title sits on the left and the count on the right.

Scrolling with scroll

scroll stacks its children top to bottom, like column, and lets the user scroll them up and down when they don’t fit. Like spacer, it takes the space its parent has left, so put it where there is space to give: straight in a window, or in a stack with grow: true.

window("Downloads", width: 400, height: 300) {
  column(spacing: 8, padding: 16) {
    label("Downloads", style: .title)
    scroll(spacing: 4) {
      for item in downloads {
        label(item.url)
      }
    }
  }
}

The title stays put, and the list below it scrolls in the rest of the window. A scroll area inside a stack that fits its content gets no space at all.

Separating sections with divider

divider() draws a thin line across the stack, to separate groups of elements.

Centering everything

To put content in the middle of a window, center on both axes:

column(align: .center, justify: .center) {
  label("Right in the middle")
}

Sizes

A stack fits its content unless you say otherwise:

SettingWhat it doesExample
widtha fixed width, in pointswidth: 200
max_widthfill the width on offer, up to this many pointsmax_width: 600
growtake the space the parent has left along its directiongrow: true

A window can’t be made narrower than its content, so a fixed-width sidebar and a text field never get squashed.

surface: draws a stack on one of the system’s own backgrounds. Here is a whole app with a sidebar, a main pane and a card:

state section = "inbox"
state unread = 3

window("Mail", width: 720, height: 460) {
  row() {
    column(surface: .sidebar, width: 200, padding: 10) {
      label("Mailboxes", style: .caption)
      nav_item("Inbox", value: "inbox", selection: $section, icon: .inbox, badge: "{unread}")
      nav_item("Sent", value: "sent", selection: $section, icon: .send)
      nav_item("Archive", value: "archive", selection: $section, icon: .archive)
    }
    column(surface: .content, grow: true, padding: 24, align: .center) {
      column(max_width: 480, spacing: 12) {
        row(align: .center) {
          label("Mail", style: .large_title)
          spacer()
          button("Mark all read", style: .icon, icon: .check) { unread = 0 }
        }
        column(surface: .card, padding: 16, spacing: 4) {
          label("Welcome to {section}", style: .headline)
          label("Cards group related content on the page.", color: .secondary)
        }
      }
    }
  }
}
  • .sidebar is the side panel. On macOS it’s translucent and runs up under the title bar, like Finder’s. Fill it with nav_item rows, which highlight the one selection holds.
  • .content is the main pane, in the color content is read on.
  • .card is a rounded, outlined panel that groups related content.

grow: true lets the main pane take the rest of the window. max_width: 480 inside a pane with align: .center keeps the text a comfortable width, centred, when the window is maximized. See column for how each surface looks on each platform.

Text sizes

label has four text styles. They use your system’s own fonts, so they follow accessibility text sizes and dark mode automatically.

StyleUse it for
.large_titlethe name of what the window shows
.titlethe main heading
.headlinea bold section heading
.body (default)normal text
.captionsmall, quieter text

To change a label’s color, use color: with one of the system’s colors, such as .secondary, .accent or .danger. To place its text, use align: with .start, .center or .end. See label for the full list.