Making content themeable in Drupal 8

hook_theme()

hook_theme() is still there and its implementation hasn't really changed since Drupal 7.

<?php
/**
 * Implements hook_theme().
 */
function mymodule_theme() {
  return [
    'mymodule_page_example' => [
      'variables' => ['vars' => NULL],
      'template' => 'page.example',
    ],
  ];
}

In the above example the template file page.example.html.twig would go into the /modules/mymodule/templates folder.

Applying the theme callback from within your controller

<?php
use Drupal\Core\Controller\ControllerBase;

class MymoduleController extends ControllerBase{

  public function pageMethod() {
    return [
      '#theme' => 'mymodule_page_example',
      '#vars' => 'Some string or other value',
    ];
  }
}

Applying it from anywhere using the renderer service

<?php
$themed_output = \Drupal::service('renderer')->render(
  [
    '#theme' => 'mymodule_page_example',
    '#vars' => 'Some string or other value',
  ]
);