With GNU tools (your --color=always is a GNU extension already) and a shell with support for Ksh-style process substitution:
xargs -r0a <(grep -rlZ pattern .) vim
Or:
xargs -r0a <(find . ... -exec grep -lZ pattern {} +) vim
With zsh:
vim ${(0)"$(find . ... -exec grep -lZ pattern {} +)"}
With bash 4.4+:
readarray -td '' files < <(find . ... -exec grep -lZ pattern {} +)
vim "${files[@]}"
Those minimise the number of grep invocations that are performed. The point is to tell grep to output the file names NUL-delimited, so they can be reliably split into separate arguments for vim by GNU xargs -0 or zsh's 0 parameter expansion flag or bash's readarray -td ''.
In zsh, you could also do:
vim ./**/*(...e['grep -q pattern $REPLY'])
(where ... stands in for further qualifiers you may want to add, like for the find approach).
That means however that like the approaches that use find -exec grep -q pattern {} ';', one grep invocation would be run per file which would make it significantly slower.
Your first approach would work in zsh provided you replaced --color=always with -Z and changed the value of IFS to IFS=$'\0' from the default of IFS=$' \r\n\0'. I wouldn't work in other shells, as they don't support storing NULs in variables, let alone $IFS and would also perform filename generation on the words resulting of the splitting which you'd need to disable with set -o noglob or set -f.
Why is looping over find's output bad practice? will give you more tips on how to process the list of files found by find reliably.