They’re not global per se, but added by the Template component when rendering a page:
// merge and register the template data globally
$tplData = tpl::$data;
tpl::$data = array_merge(tpl::$data, $data);
// load the template
$result = tpl::load($file, null, $return);
// reset the template data
tpl::$data = $tplData;
(In this example $data contains 'site', 'page', 'kirby' and 'pages' keys with the corresponding objects.)
On the other hand, the snippet helper function will simply load existing data from tpl::$data.
This means that if you have a route that renders a snippet, and you’re not rendering a page (or a rendering a snippet before rendering a page and calling the Template component), tpl::$data will probably be empty. If that doesn’t work for you, you probably need to write your own rendering function that does the same thing as Kirby\Component\Template::render. Or see if you can retrieve an instance of Kirby\Component\Template (through the registry?) and use the render method to render a snippet (by giving the full path to the snippet PHP file as first argument).
By the way, it seems you’re feeding your snippet a $pages collection with a specific pages collection. I would avoid doing that, especially if you’re going to overwrite tpl::$data['pages'] with that specific value. Maybe use a different name? Or, if the logic in your snippet is to always use the children of the current page, maybe hardcode that in the snippet?
This issue seems to be coming back to me from time to time. This time I need to use it to preview a snippet. For that I will need a route and somehow send arguments to the snippets. Because the snippet can use many other snippets which will probably use the $page object I will need to solve this issue.
Anyway I think your previous reply gave a few hints of how to solve it. Time will tell of how things goes.