`dropShadow({ blur: … })` is silently ignored, though the `shadow` shorthand and the `blur` prop both call that value the blur
What happens
Writing blur in an effect helper does nothing, and nothing says so. The value is dropped, the
default radius of 8 is drawn, and the render succeeds without a warning.
What makes this a trap rather than a simple naming preference is that the dialect itself uses that word for this value in two other places:
- the
shadowstring shorthand isx y blur color, and its third value becomes the shadow'sradius; - there is a top-level
blurprop, which is a layer blur's radius.
So someone who has written shadow="4 4 12 #000" — or who knows CSS box-shadow, where the third
value is the blur radius — writes dropShadow({ x: 4, y: 4, blur: 12, color: '#000' }) and gets a
shadow with a radius of 8. The name the dialect taught them is the one name the helper ignores.
Underneath it is a general silence: the helpers build their effect by reading the option names they
know, so any other key is read by nobody and reported by nobody. A typo behaves the same way —
colour, offsetY, radious all vanish without a word.
TypeScript does not catch this. ShadowEffectOptions would reject an unknown key in a compiled
call site, but the dialect is not compiled by the caller: render.ts transforms the JSX with sucrase
and evaluates it with new Function, passing the helpers in as __helpers. The object literal
reaching dropShadow is built at runtime, from text a user or an agent wrote, so the excess-property
check never runs. Runtime is the only place this can be caught, and nothing catches it there.
This matters more than a typo elsewhere would, because the output is a picture. The render looks plausible, and the difference between a 12px and an 8px shadow is not something a caller checks. A value silently replaced by a default is indistinguishable from a value that was never written.
Reproduction
In OpenPencil, render this design JSX:
<Frame w={100} h={60} bg="#FFD23F" effects={[dropShadow({ x: 4, y: 4, blur: 12, color: '#14101F' })]} />Read the frame back — through
get_node, or through the MCP server, or by exporting the design. The effect'sradiusis8, the default, not12.No warning appears anywhere in the result.
The same value under the name the helper knows does what it says:
<Frame w={100} h={60} bg="#FFD23F" effects={[dropShadow({ x: 4, y: 4, radius: 12, color: '#14101F' })]} />gives
radius: 12.For the shorthand that teaches the other name, on the same frame:
shadow="4 4 12 #14101F"givesradius: 12.A plain typo behaves like step 1:
dropShadow({ colour: '#FF0000' })draws the default black shadow and says nothing.
What was expected
An option a caller writes is either applied, or reported by name. Either the helpers accept blur
as a name for the shadow's radius — which would match the shadow shorthand, the blur prop and
CSS — or they warn that blur was ignored and name the options that exist; ideally both. The same
for any key the helper does not know.
Where
packages/core/src/design-jsx/effects.ts—shadowEffectat lines 31–42 builds the effect from named options only;blurEffectat lines 44 onwards does the same, keepingradiusandvisible. The helpers aredropShadow,innerShadow,layerBlur,backgroundBlurandforegroundBlur.- The two places the dialect calls this value a blur:
packages/core/src/design-jsx/props-overrides.ts— theshadowshorthand at lines 560–576, whoseparts[2]becomesradius, and theblurprop at lines 578–590. - Where the options stop being typed:
packages/core/src/design-jsx/render.tsat line 101. - Seen at commit
e2de247.
Source: open-pencil/open-pencil