You don't know when the HTML template is rendered whether a facet is active or not. So in mytheme_preprocess_html() assume it is not:
$variables['attributes']['class'][] = 'facet-is-not-active-class';
In the facets block replace the placeholder class if the block is visible:
function mytheme_preprocess_block(&$variables) {
if ($variables['configuration']['provider'] == 'facets') {
if (!in_array('hidden', (array) $variables['attributes']['class'])) {
$variables['#attached']['placeholders']['facet-is-not-active-class'] = [
'#markup' => 'facet-active',
];
}
}
}
Important is that the placeholder string facet-is-not-active-class
is unique, use a longer name or even a hash. Placeholders are replaced for all occurrences of the string on the page.
Issue with nested placeholders
This works only for blocks not auto-placeholdered. The facet block, unfortunately, is always placeholdered. It sets cache max-age 0 which meets the default condition for auto-placeholdering. See https://www.drupal.org/docs/drupal-apis/render-api/auto-placeholdering#s-what
To prevent that you can set #create_placeholder to FALSE:
/**
* Implements hook_block_build_BASE_BLOCK_ID_alter().
*/
function mymodule_block_build_facet_block_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
$build['#create_placeholder'] = FALSE;
}
Caveat: This is a real performance killer, it doubles the page loading times in my tests. So if performance is important you probably should look for a client-side solution, better CSS or a javascript library which adds the necessary classes.