---
title: "DroppyShellMetrics, DroppyKit"
description: "The geometry Droppy draws its surfaces at."
canonical: "https://getdroppy.app/docs/droppykit/api/droppy-shell-metrics"
last-updated: "2026-09-09"
---

# DroppyShellMetrics

The geometry Droppy draws its surfaces at.

`enum DroppyShellMetrics`

Mirrored by hand from the app (`NotchLayoutConstants`, `NotchShelfView`, `HUDLayoutCalculator`); `scripts/sync_droppykit.sh` does not cover it. A droplet rarely needs these directly, the host owns the outer shape, but a preview does, and so does any content that has to keep clear of a corner.

The one thing to take from this type if you take nothing else: **the shell is not one shape**. Its top shoulders more than triple when it opens, its bottom corners more than double, and the content insets differ by mode by more than the width of most controls. Laying a surface out against one set of numbers and shipping it is how a widget ends up under a camera housing.

## Type properties

### closedIslandCornerRadius

`static let closedIslandCornerRadius: CGFloat`

Corner radius of the at-rest Dynamic Island pill.

### expandedHUDBottomRadius

`static let expandedHUDBottomRadius: CGFloat`

Bottom radius of an expanded HUD's card, which hangs less far than the shelf and curves a little tighter for it.

### expandedIslandCornerRadius

`static let expandedIslandCornerRadius: CGFloat`

Corner radius of the expanded Dynamic Island, a touch softer than the shelf so it reads closer to the phone's capsule.

### expandedNotchTopRadius

`static let expandedNotchTopRadius: CGFloat`

Outward wing radius at the top of ANY expanded notch shell, the shelf, an expanded HUD, a takeover.

Three times the closed radius, and the single biggest reason an expanded preview drawn with the closed number looks wrong. The host interpolates between the two as the shelf opens.

### expandedShelfCornerRadius

`static let expandedShelfCornerRadius: CGFloat`

Bottom radius of the EXPANDED shelf on a notch-styled surface.

### expandedShoulderInset

`static let expandedShoulderInset: CGFloat`

Leading and trailing inset every expanded notch surface keeps, so content clears the 32pt shoulder the shell opens to.

`contentPadding (20) + shoulder (32) − the 4pt every nested surface shaves`, which is how the host derives it.

### islandTopMargin

`static let islandTopMargin: CGFloat`

Gap the island keeps below the screen's top edge.

Four points, not the menu bar's height. The island is a floating pill tucked right under the edge, and a preview that drops it a menu bar's worth reads as a widget parked in the middle of the desktop.

### physicalNotchHeight

`static let physicalNotchHeight: CGFloat`

Height of a MacBook's physical notch, and the height a notch-styled shell draws at when there is no physical cutout to match.

The host measures the real cutout per display and this is its fallback, so treat it as typical rather than exact. Every derived radius below takes a height for that reason.

### shelfFullBleedCornerRadius

`static let shelfFullBleedCornerRadius: CGFloat`

Corner radius of a card that bleeds to the shell's edge, where it DOES have to answer the shell's curve: concentric with the expanded island at the distance a solo card sits from its edge (the island's 16pt inset plus the 1.5pt slot inset), so its corners keep the gap its sides keep. 26.5 since 1.20.1; it was 34, rounder than the island at that distance.

It is the radius for a fill you draw flush to a solo island card, not a clip: the island shell clips that card, so [`contentInsets`](https://getdroppy.app/docs/droppykit/api/shelf-widget-context#content-insets) is zero there.

### shelfGroupedCardSpacing

`static let shelfGroupedCardSpacing: CGFloat`

The visible gap between two cards in a grouped row: the shelf's 14pt slot spacing plus the 2pt inset each inner slot edge keeps.

### shelfWidgetCornerRadius

`static let shelfWidgetCornerRadius: CGFloat`

Corner radius of a shelf widget's card, inside the shell.

Deliberately much tighter than the shell's own 40pt: the card is a rectangle in a grid, and matching the shell's curve at this size eats the card's corners rather than echoing them.

[`contentInsets`](https://getdroppy.app/docs/droppykit/api/shelf-widget-context#content-insets) is the clearance for this corner (8 on every edge, where it needs 5.25), so a change here changes that too.

### shelfWidgetFixedContentHeightRange

`static let shelfWidgetFixedContentHeightRange: ClosedRange<CGFloat>`

What [`fixed(_:)`](https://getdroppy.app/docs/droppykit/api/shelf-widget-content-height#fixed) is clamped to, in points at the Regular shelf size. Below the floor a card cannot hold one row of text; above the ceiling the shelf would cover most of a 13-inch screen.

### shelfWidgetSlotInset

`static let shelfWidgetSlotInset: CGFloat`

Inset the shelf keeps between a widget's slot and the card inside it, on every edge.

Every number a widget declares in [`ShelfWidgetLayoutTraits`](https://getdroppy.app/docs/droppykit/api/shelf-widget-layout-traits) is the widget's rectangle, in points at the Regular shelf size; these metrics call that rectangle the card, though nothing is drawn around it. The slot around it is this much larger on each edge, and a solo shell is the rectangle plus twice this plus `shelfContentInsets(usesNotchStyle:notchHeight:)`.

### shelfWidgetStandardContentHeight

`static let shelfWidgetStandardContentHeight: CGFloat`

Height of a widget's card when it declares [`standard`](https://getdroppy.app/docs/droppykit/api/shelf-widget-content-height#standard), at the Regular shelf size.

The host's uniform widget canvas is 96pt; the slot inset above and below takes 1.5 each, and this is what is left for the card.

## Type methods

### closedNotchBottomRadius(height:)

`static func closedNotchBottomRadius(height: CGFloat = physicalNotchHeight) -> CGFloat`

Inward bottom radius of the CLOSED notch, derived the same way and deliberately a little larger than the shoulder.

### closedNotchTopRadius(height:)

`static func closedNotchTopRadius(height: CGFloat = physicalNotchHeight) -> CGFloat`

Outward wing radius at the top of the CLOSED notch shell.

Derived from the cutout's height, clamped to 8...10, because the shell traces hardware and hardware differs: a 13.6" Air's notch is shorter than a 16" Pro's, and a fixed radius reads as a mismatch on one of them.

### expandedHUDContentInsets(usesNotchStyle:notchHeight:)

`static func expandedHUDContentInsets(usesNotchStyle: Bool, notchHeight: CGFloat = physicalNotchHeight) -> EdgeInsets`

Insets between an expanded HUD's shell and its card.

The same shoulders as the shelf, and the same 16pt floor, but the top clears the cutout exactly rather than the cutout plus a gap: a HUD's card is shorter and the extra 10pt the shelf keeps would cost it a row.

### shelfContentInsets(usesNotchStyle:notchHeight:)

`static func shelfContentInsets(usesNotchStyle: Bool, notchHeight: CGFloat = physicalNotchHeight) -> EdgeInsets`

Insets between the expanded SHELF's edge and the widget row inside it.

The two modes are not one number. A notch shelf clears the physical cutout plus a 10pt gap at the top and the 32pt shoulders at the sides; the island has neither and keeps a uniform 16.

### shelfMinimumSoloCardWidth(usesNotchStyle:)

`static func shelfMinimumSoloCardWidth(usesNotchStyle: Bool) -> CGFloat`

The narrowest card a solo widget is ever handed: the minimum shell minus this mode's side insets and the slot inset on both sides. A widget that declares a narrower solo width gets this much, because the shell does not shrink around it.

### shelfMinimumWidth(usesNotchStyle:)

`static func shelfMinimumWidth(usesNotchStyle: Bool) -> CGFloat`

The narrowest shell the expanded shelf opens to with one widget on it.

The island's 405 plus, on a notch, the 46pt the opened 32pt shoulders add over the closed 9pt ones. The shell never shrinks below this around a narrow card; the card is stretched to fill the slot instead.
