Make iframe CSS overrides easier

Created on 29 June 2023, 12 months ago
Updated 16 January 2024, 5 months ago

Problem/Motivation

Admin themes like Gin apply left padding and margin on the body tag. Admin themes like that have no context that they will ever be displayed in a modal. This makes for some awkward modal viewing experiences.

Steps to reproduce

Install Gin, Gin Toolbar. Set the toolbar to be vertical. Add a section or block with Layout Builder - the Gin UI will be pushed far from the left of the modal.

Proposed resolution

Add the theme name (and possibly other variants) to the body tag for easier theming.

Here is an example I did in a custom module to get around this for Gin (with a vertical toolbar). Horizontal toolbars may have different values.


/**
 * Implements hook_preprocess_html().
 */
function mymodule_preprocess_html(&$variables) {
  $active_theme = \Drupal::theme()->getActiveTheme()->getName();
  $variables['attributes']['class'][] = $active_theme;
}

/**
 * Implements hook_page_attachments().
 */
function mymodule_page_attachments(&$attachments) {
  $headers = \Drupal::request()->server->getHeaders();

  if (preg_match('/(.*)\/\d+\/layout/', $headers["REFERER"])) {
    $attachments['#attached']['library'][] = 'mymodule/layout_builder_overrides';
  }
}

In the CSS file it loads:

body.gin {
  padding-left: 0 !important;
}

Before:

After:

This is just the start for me of some Gin related tweaks I am making, but would not be possible otherwise. It leaves the original Gin UI intact elsewhere. Additionally, it may be useful to also do something this to support more ways of styling depending on context:

/**
 * Implements hook_preprocess_html().
 */
function mymodule_preprocess_html(&$variables) {
  $active_theme = \Drupal::theme()->getActiveTheme()->getName();
  $variables['attributes']['class'][] = $active_theme;

  $headers = \Drupal::request()->server->getHeaders();

  if (preg_match('/(.*)\/\d+\/layout/', $headers["REFERER"])) {
    $variables['attributes']['class'][] = $active_theme . '--layout-builder-iframe-modal';
  }
}

The referer match was something I came up with quick to start resolving this problem for a project I am on. If there is a better way of intercepting a Layout Builder request than reading headers I am all for it.

πŸ“Œ Task
Status

Needs review

Version

1.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States kevinquillen

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @kevinquillen
  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen
  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen
  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen

    I also tried to read the SEC_FETCH_DEST header (which has 'iframe' as a value locally), but this is stripped out apparently on managed hosting like Acquia.

  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen

    Using REFERER interferes on other pages (like navigating from node/*/layout to somewhere else).

    Instead, I am now doing:

    /**
    * Implements hook_page_attachments().
    */
    function mymodule_page_attachments(&$attachments) {
    $request = \Drupal::request();
    $request_uri = $request->getRequestUri();

    if ($request->query->get('destination') == '/layout_builder_iframe_modal/redirect') {
    $attachments['#attached']['library'][] = 'mymodule/layout_builder_overrides';
    }
    }

  • Status changed to Needs review 5 months ago
  • πŸ‡¨πŸ‡¦Canada kpaxman

    I believe I have the same concern as you, though I have to admit I'm not clear on how your posted workaround solves the issue.

    I went, I think, in the other direction - if the contents have a class identifying it as being in the modal, then any modal-specific CSS can be written in your admin theme, e.g. to remove margin when in the modal but not in "regular" use, something like .layout-builder-iframe-modal .whatever { margin: 0; }.

    I'm attaching a patch that adds this class.

  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen

    In my case I only want to attach the library when the modal is active, then the CSS is applied. I wound up here in the end:

    /**
     * Implements hook_preprocess_html().
     */
    function mos_layout_builder_preprocess_html(&$variables) {
      $active_theme = \Drupal::theme()->getActiveTheme()->getName();
      $variables['attributes']['class'][] = $active_theme;
    
      $request = \Drupal::request();
    
      if ($request->query->get('destination') == '/layout_builder_iframe_modal/redirect') {
        $variables['html_attributes']->addClass($active_theme . '--layout-builder-iframe-modal');
        $variables['attributes']['class'][] = $active_theme . '--layout-builder-iframe-modal';
      }
    }
    

    Sample CSS:

    body.gin--layout-builder-iframe-modal {
      padding-left: 0 !important;
    }
    
    html body.gin--vertical-toolbar.gin--layout-builder-iframe-modal {
      padding-left: 0 !important;
    }
    
    html body.gin--vertical-toolbar.gin--layout-builder-iframe-modal .field-group-tabs-wrapper .draggable,
    html body.gin--vertical-toolbar.gin--layout-builder-iframe-modal .field-group-tabs-wrapper .draggable td *:not(div.form-wrapper) > div.form-wrapper,
    html body.gin--vertical-toolbar.gin--layout-builder-iframe-modal .draggable,
    html body.gin--vertical-toolbar.gin--layout-builder-iframe-modal .draggable td *:not(div.form-wrapper) > div.form-wrapper {
      width: calc(100% - 45px) !important;
    }
    

    I don't recall why I added the theme name to it.

Production build 0.69.0 2024