packages/codemods/v5-0-0/22-migrate-nested-css-selectors.md
style: BlocksLowdefy v4 used Emotion CSS-in-JS under the hood, which allowed nested CSS selectors like &:hover, &:active, & > *, and & > div inside style: blocks. In v5, style: values are applied as plain inline CSS — nested selectors are no longer supported and the build rejects them:
[ConfigError] Block "my_block": Style property "&:hover" has a nested object value.
CSS properties must be simple values (strings, numbers) or operators.
The fix: convert nested CSS selectors to Tailwind utility classes or move them to public/styles.css.
app — scan YAML/YML files for nested selectors in style: and styles: blocks.
Grep for patterns like '&:hover', '&:active', '&:focus', '& > *', '& > div', or any '& inside style: or styles: blocks.
&:hover, &:active, &:focus) → Tailwind classesReplace pseudo-selector styles with Tailwind arbitrary value classes. Keep flat CSS properties in style:.
& > *, & > div) → Tailwind arbitrary child selectors or wrapper BoxTwo options:
[&>*]: prefix — for simple child styles (padding, color, margin)public/styles.cssFor complex selectors that can't be expressed as Tailwind classes, move them to public/styles.css and reference via class:.
Glob: **/*.{yaml,yml,njk}
Grep: '&: or '& inside style blocks
&:hover on a box- id: my_box
type: Box
style:
padding: '12px 16px'
borderBottom: '1px solid #f0f0f0'
cursor: pointer
transition: background 0.15s ease
'&:hover':
background: '#f7f9fc'
- id: my_box
type: Box
class: 'cursor-pointer transition-[background] duration-150 ease-in-out hover:bg-[#f7f9fc]'
style:
padding: '12px 16px'
borderBottom: '1px solid #f0f0f0'
&:active on a card- id: my_card
type: Card
style:
marginBottom: 32
boxShadow: '0 0 10px #A4B6CC'
borderRadius: 12
border: '3px solid #001528'
'&:active':
boxShadow: '0 0 10px #8391A3'
- id: my_card
type: Card
class: 'active:shadow-[0_0_10px_#8391A3]'
style:
marginBottom: 32
boxShadow: '0 0 10px #A4B6CC'
borderRadius: 12
border: '3px solid #001528'
& > * child selector- id: my_upload
type: S3UploadDragger
style:
.element:
'& > *':
padding: 3px !important
color: '#8C8C8C'
- id: my_upload
type: S3UploadDragger
class:
element: '[&>*]:!p-[3px] [&>*]:text-[#8C8C8C]'
& > div targeting a wrapper div (e.g., Affix)- id: actions
type: Affix
style:
position: fixed
bottom: 0
width: 100%
.element:
'& > div':
background: '#fff'
borderTop: '3px solid #001528'
padding: 10
properties:
offsetBottom: 0
blocks:
- id: submit_btn
type: Button
- id: actions
type: Affix
style:
position: fixed
bottom: 0
width: 100%
properties:
offsetBottom: 0
blocks:
- id: actions_wrapper
type: Box
style:
background: '#fff'
borderTop: '3px solid #001528'
padding: 10
blocks:
- id: submit_btn
type: Button
public/styles.css for complex selectorsFor selectors that can't be expressed as Tailwind classes:
/* public/styles.css */
.search-result {
cursor: pointer;
transition: background 0.15s ease;
}
.search-result:hover {
background: #f7f9fc;
}
.search-result:active {
background: #e8ecf1;
}
- id: my_result
type: Box
class: search-result
style:
padding: '12px 16px'
borderBottom: '1px solid #f0f0f0'
class: string (e.g., 'hover:bg-[#f7f9fc] active:bg-[#e8ecf1]')!important in nested values: use Tailwind's ! prefix (e.g., [&>*]:!p-[3px]).element or .label style slots: convert the slot-specific selector to class: with the matching slot key& .ant-card-body): move to public/styles.css — Tailwind arbitrary selectors can work but are harder to read for complex antd class targetingtransition property: if moving cursor and transition to class, use Tailwind equivalents (cursor-pointer, transition-[background], duration-150, ease-in-out) or keep transition in style: — both workblocks:No nested object values should remain in style: blocks:
grep -rn "'&:" --include='*.yaml' --include='*.yml' --include='*.njk' .
grep -rn "'& " --include='*.yaml' --include='*.yml' --include='*.njk' .
Build the app — no Style property "..." has a nested object value errors should appear