A shadow here is a stack of two or three layers rather than one offset and blur, which is why a PDS surface does not look like a box-shadow guess. There are three of these stacks and no way to ask for something between two of them.
- boxShadow.Elevation.100 3 layers, 8px deep
- boxShadow.Elevation.200 2 layers, 10px deep
- boxShadow.Elevation.300 3 layers, 24px deep
Shown on a light surface in both themes. PDS defines one set of shadows, tuned for light backgrounds, and ships no dark-mode variant.
The middle elevation step is the only one tinted black
Section titled “The middle elevation step is the only one tinted black”boxShadow.Elevation.100 and .300 are built from rgba(191, 191, 199, …), a cool grey that is not itself a color token. .200 is built from rgba(0, 0, 0, …).
Put .100 and .200 on neighboring surfaces and the difference reads as a change of material rather than a change of height, because one shadow is grey and the other is a darkened version of whatever is behind it. Three distances from one light source would not do that, so this is a defect in the token rather than a choice to design around.
Depth does not follow layer count
Section titled “Depth does not follow layer count”.100 has three layers and drops 8px. .200 has two layers and drops 10px. .300 has three layers and drops 24px.
So the step from .100 to .200 removes a layer while going deeper, and the step from .200 to .300 adds one back and more than doubles the depth. The scale is not a ramp — it is three shadows that were drawn for three specific jobs, and the gap between .200 and .300 is where most of the visual distance lives.
The React library ships its own shadow scale
Section titled “The React library ships its own shadow scale”@persona/design-system imports Color, Spacing, Typography, BorderRadius and FontFamilies from the token package. It does not import BoxShadow. Nothing in the library references boxShadow.Elevation.100, .200 or .300.
It has its own scale instead, three constants named Elevation.Low, Elevation.Mid and Elevation.High, and Toast, Floating Panel, Data Table, Swatch Picker, Table and the hoverable mixin read those. Low uses Elevation.100’s exact offsets and blurs with black in place of the grey and roughly a third of the opacity. Mid and High share no layer at all with .200 or .300.
Two elevation scales exist, neither references the other, and neither is generated from the other. When you are matching a PDS surface, the shadow on the page came from the React library; when you are drawing in Figma, the shadow came from here. That is worth knowing before you conclude your shadow is wrong.
The box shadow group is not only elevation
Section titled “The box shadow group is not only elevation”boxShadow in the token package also holds Divider and Hairline for one-pixel edges, Button.Focus in three colors, and Tooltip.Hover. None of those is a step on a depth scale, and the specimen above deliberately shows only the Elevation* entries.
Two of them cannot be rendered at all. boxShadow.Button.Hover and boxShadow.Tooltip.Hover ship the literal string rgba(color.Primary.Black, 0.08) — an unresolved token reference inside a CSS function, which no browser can parse. They are shipped and broken, not shipped and unused.
Shadows are the one scale with no CSS custom property
Section titled “Shadows are the one scale with no CSS custom property”The package emits --color-* for all 272 color tokens, --spacing-0 through --spacing-9, and --border-radius-sm through --border-radius-circle. It emits no --box-shadow-* at all, so a shadow cannot be referenced from a stylesheet the way every other token can. It has to be composed in JavaScript from the layer objects, which is what the specimen above does, and it is why a shadow is the easiest token in this system to end up hand-writing.
Was this page helpful?